diff --git a/.eslintrc.js b/.eslintrc.js index ae4c4cfdd2..cc55b4443c 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -54,6 +54,7 @@ module.exports = { required: 'id', allowChildren: false, }], + 'no-param-reassign': [2, { props: false }], }, env: { jest: true, diff --git a/.github/workflows/analyze-dependents.yml b/.github/workflows/analyze-dependents.yml index 53e9921226..6839a0ec20 100644 --- a/.github/workflows/analyze-dependents.yml +++ b/.github/workflows/analyze-dependents.yml @@ -191,6 +191,16 @@ jobs: with: repository: edx/studio-frontend path: dependent-usage-analyzer/.projects/studio-frontend + - name: Checkout edx/frontend-app-communications + uses: actions/checkout@v2 + with: + repository: edx/frontend-app-communications + path: dependent-usage-analyzer/.projects/frontend-app-communications + - name: Checkout edx/frontend-app-learner-dashboard + uses: actions/checkout@v2 + with: + repository: edx/frontend-app-learner-dashboard + path: dependent-usage-analyzer/.projects/frontend-app-learner-dashboard - name: Verify checkouts working-directory: dependent-usage-analyzer run: ls -la .projects diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cfca2cafe7..bf0ff00660 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,6 +24,9 @@ jobs: run: | npm ci npm run generate-component-install + npm run doc-site-install + - name: Check Types + run: npm run type-check - name: Lint run: npm run lint - name: Test diff --git a/.github/workflows/manual-publish.yml b/.github/workflows/manual-publish.yml new file mode 100644 index 0000000000..f6863d4333 --- /dev/null +++ b/.github/workflows/manual-publish.yml @@ -0,0 +1,40 @@ +name: Manual Publish +on: [workflow_dispatch] +jobs: + release: + name: Manual Publish + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Setup Node.js + uses: actions/setup-node@v2 + with: + node-version: 16 + - name: Install dependencies + run: npm ci + - name: Validate package-lock.json changes + run: make validate-no-uncommitted-package-lock-changes + - name: Lint + run: npm run lint + - name: Test + run: npm run test + - name: i18n_extract + run: npm run i18n_extract + - name: Coverage + uses: codecov/codecov-action@v2 + - name: Build + run: npm run build + # NPM expects to be authenticated for publishing. This step will fail CI if NPM is not authenticated + - name: Check NPM authentication + run: | + echo "//registry.npmjs.org/:_authToken=${{ secrets.SEMANTIC_RELEASE_NPM_TOKEN }}" >> .npmrc + npm whoami + - name: Release + env: + GITHUB_TOKEN: ${{ secrets.SEMANTIC_RELEASE_GITHUB_TOKEN }} + NPM_TOKEN: ${{ secrets.SEMANTIC_RELEASE_NPM_TOKEN }} + # `npm publish` relies on version specified in package.json file + run: npm publish --tag old-version diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6fccfebc4e..058eac39f8 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -25,6 +25,7 @@ jobs: run: | npm ci npm run generate-component-install + npm run doc-site-install - name: Lint run: npm run lint - name: Test diff --git a/.github/workflows/update-browserslist-db.yml b/.github/workflows/update-browserslist-db.yml new file mode 100644 index 0000000000..db4346efe0 --- /dev/null +++ b/.github/workflows/update-browserslist-db.yml @@ -0,0 +1,12 @@ +name: Update Browserslist DB +on: + schedule: + - cron: '0 0 * * 1' + workflow_dispatch: + +jobs: + update-browserslist: + uses: openedx/.github/.github/workflows/update-browserslist-db.yml@master + + secrets: + requirements_bot_github_token: ${{ secrets.requirements_bot_github_token }} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000000..49e27eda3f --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,13 @@ +# Contributing + +## Open edX + +Please refer to the ["How to Contribute"](https://openedx.org/r/how-to-contribute) documentation and [Code of Conduct](https://openedx.org/code-of-conduct/) from Open edX for general information on contributing to the Open edX project. + +## Paragon + +The Paragon Working Group accepts bug fixes, new features, documentation, and security patches. You may find open issues [here](https://github.com/openedx/paragon/issues) or by visiting the Paragon Working Group [project board](https://github.com/orgs/openedx/projects/43/views/15). Issues with the "help wanted" or "byte-size" labels may be good candidates to pick up. + +If you'd like to contribute a new component or update an existing component, please consider reaching out to the Paragon Working Group via the channels [described in the README](./README.md#getting-help) to propose your changes. + +The Paragon Working Group looks forward to your contributions! 💎 diff --git a/Makefile b/Makefile index 2989e4a9d3..ef1fd811c4 100644 --- a/Makefile +++ b/Makefile @@ -48,7 +48,7 @@ push_translations: # Pulls translations from Transifex. pull_translations: | requirements - tx pull -f --mode reviewed --languages=$(transifex_langs) + tx pull -t -f --mode reviewed --languages=$(transifex_langs) # compile files with translated strings to KEYVALUEJSON format which react-intl understands... npm run-script i18n_compile diff --git a/README.md b/README.md index 050476a3d7..3ba1851bbb 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,19 @@ # Paragon -[](@edx/paragon) [](https://www.npmjs.com/package/@edx/paragon) [](https://github.com/openedx/paragon/actions/workflows/release.yml) [](https://codecov.io/gh/edx/paragon) +[](https://github.com/openedx/paragon/actions/workflows/release.yml) +[](@edx/paragon) + + +[](https://codecov.io/gh/edx/paragon) +[](https://www.npmjs.com/package/@edx/paragon) -Paragon is a pattern library containing [accessible](https://www.w3.org/WAI/standards-guidelines/aria/) React components and a SCSS foundation built on Twitter Bootstrap. Paragon is developed for the Open edX platform. +## Purpose -Documentation lives at https://paragon-openedx.netlify.app/. +Paragon is a pattern library containing [accessible](https://www.w3.org/WAI/standards-guidelines/aria/) React components and a SCSS foundation built on Twitter Bootstrap. Paragon is developed for the [Open edX](https://openedx.org/) platform. +Documentation lives at https://paragon-openedx.netlify.app/. -## Usage +## Getting Started ### React Components @@ -36,7 +42,9 @@ Usage for Open edX and others: @import '~@edx/paragon/scss/core/core.scss'; ``` -Usage on edx.org: +Usage on with `@edx/brand`: + +**index.scss** ``` @import '~@edx/brand/paragon/fonts.scss'; @@ -47,133 +55,20 @@ Usage on edx.org: Note that including fonts will affect performance. In some applications may choose not to load the custom font to keep it highly performant. -## Contributing & Development - -See the [code of conduct](https://github.com/openedx/.github/blob/master/CODE_OF_CONDUCT.md). - -#### 1. Start the documentation site development server - -The Paragon documentation site serves both as documentation and as a workbench to create your component within. To see your component in action, you need to run the documentation site locally. (Note you need to install dependencies both in the project root and the `www` directory) - -``` -npm run install-all -cd www -npm start -``` - -#### 2. Create new component - -To create new component run - -``` -npm run generate-component MyComponent -``` - -where `MyComponent` is your new component's name. - -This will create a directory in `/src/` that will contain templates for all necessary files to start developing the component: -``` -MyComponent -├── index.jsx -├── README.md -├── MyComponent.scss -├── _variables.scss -└── MyComponent.test.jsx -``` - -The script will also automatically export your component from Paragon. - -#### 3. Start developing - -`/src/MyComponent/index.jsx` is where your component lives, the file is created with the following template, edit it to implement your own component. - -``` jsx -import React from 'react'; -import PropTypes from 'prop-types'; -import classNames from 'classnames'; - -const MyComponent = React.forwardRef(({ className, children }, ref) => ( -
Example subtitle
} + > + {cardContent} ++ Example subtitle +
++ Example subtitle. +
+Hello world
, + }; + const wrapper = mount({name}
- {name}
+ {name}
- {name}
+ {value}
- {value}
+ []
- []
+
- Object.{'<'}
-
- {'>'}
-
-
+ Object.{'<'}
+
+ {'>'}
+
+ {name}
- {name}
+ {name}
- {name}
+ {raw}
- {raw}
+ {content}
} - {componentProps.length > 0 ? ( -{content}
} + {componentProps.length > 0 ? ( +{text}
); + +export type HipsterIpsumContentType = { + shortParagraphsInContent: string[], + numShortParagraphs: number, + paragraphsInContent: string[], numParagraphs: number, }; -const HipsterIpsum = ({ numParagraphs }: HipsterIpsumTypes) => { - const shuffledParagraphs = React.useMemo(() => shuffle(paragraphs), []); - const content = shuffledParagraphs - .slice(0, numParagraphs) - .map(text =>{text}
); +const useHipsterIpsumContent = ({ + shortParagraphsInContent, + numShortParagraphs, + paragraphsInContent, + numParagraphs, +}: HipsterIpsumContentType) => { + if (numShortParagraphs !== undefined) { + const shuffledShortParagraphs = shuffle(shortParagraphsInContent); + return getHipsterIpsumContent(shuffledShortParagraphs, numShortParagraphs); + } - return ( - <> - {content} -Sourced with love from https://hipsum.co/
- > - ); + const shuffledParagraphs = shuffle(paragraphsInContent); + return getHipsterIpsumContent(shuffledParagraphs, numParagraphs); }; +function HipsterIpsum({ + numParagraphs, + numShortParagraphs, +}: HipsterIpsumType) { + const content = useHipsterIpsumContent({ + shortParagraphsInContent: shortParagraphs, + numShortParagraphs, + paragraphsInContent: paragraphs, + numParagraphs, + }); + return content; +} + HipsterIpsum.propTypes = { numParagraphs: PropTypes.number, + numShortParagraphs: PropTypes.number, }; HipsterIpsum.defaultProps = { numParagraphs: 2, + numShortParagraphs: undefined, }; export default HipsterIpsum; diff --git a/www/src/components/exampleComponents/MiyazakiCard.tsx b/www/src/components/exampleComponents/MiyazakiCard.tsx index 1c47069a18..0fc9482f97 100644 --- a/www/src/components/exampleComponents/MiyazakiCard.tsx +++ b/www/src/components/exampleComponents/MiyazakiCard.tsx @@ -1,7 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -// @ts-ignore -import { Card } from '~paragon-react'; // eslint-disable-line +import { Card } from '~paragon-react'; export interface IMiyazakiCard { className: string, @@ -12,12 +11,12 @@ export interface IMiyazakiCard { }, } -const MiyazakiCard = ({ className, original }: IMiyazakiCard) => { +function MiyazakiCard({ className, original }: IMiyazakiCard) { const { title, director, release_date: releaseDate } = original; return (You just hit a route that doesn't exist... the sadness.
-You just hit a route that doesn't exist... the sadness.
+{row.values.identifier}
;
-const MinWidthCell = ({ row }) => {row.values.minWidth ? `${row.values.minWidth}px` : '-'}
;
-const MaxWidthCell = ({ row }) => {row.values.maxWidth ? `${row.values.maxWidth}px` : '-'}
;
+function IdentifierCell({ row }) {
+ return {row.values.identifier}
;
+}
+function MinWidthCell({ row }) {
+ return {row.values.minWidth ? `${row.values.minWidth}px` : '-'}
;
+}
+function MaxWidthCell({ row }) {
+ return {row.values.maxWidth ? `${row.values.maxWidth}px` : '-'}
;
+}
-const Responsive = () => {
+function Responsive() {
const breakpointsData = Object.keys(breakpoints).map(breakpoint => {
const { minWidth, maxWidth } = breakpoints[breakpoint];
const breakpointData = getBreakpointDescription(breakpoint);
@@ -71,7 +77,7 @@ const Responsive = () => {
);
-};
+}
const cellPropTypes = {
row: PropTypes.shape({
diff --git a/www/src/pages/foundations/spacing.tsx b/www/src/pages/foundations/spacing.tsx
index a1e3cbfac2..c3ba85271a 100644
--- a/www/src/pages/foundations/spacing.tsx
+++ b/www/src/pages/foundations/spacing.tsx
@@ -1,7 +1,6 @@
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
-// @ts-ignore
import { Form, Container, DataTable } from '~paragon-react'; // eslint-disable-line
import SEO from '../../components/SEO';
import Layout from '../../components/PageLayout';
@@ -30,21 +29,23 @@ export type PixelCellTypes = {
spacer: number,
};
-const PixelCell = ({ spacer }: PixelCellTypes) => (
-
- {measurements.margin}
-
- )}
- >
-
-
+ {measurements.margin}
+
+ )}
+ >
+
+
- {utilityClass ? `.${utilityClass}` : null}
-
-);
+function SpaceBlock({ utilityClass }: SpaceBlockTypes) {
+ return (
+
+ {utilityClass ? `.${utilityClass}` : null}
+
+ );
+}
SpaceBlock.propTypes = {
utilityClass: PropTypes.string,
@@ -137,8 +140,10 @@ export default function SpacingPage() {
))}
- - Technical Documentation{' '} -
-- An accessible, theme-ready, and open source design system built for - learning applications. -
-+ Technical Documentation{' '} +
++ An accessible, theme-ready, and open source design system built for + learning applications. +
+