diff --git a/.eslintrc b/.eslintrc index 0b3f4f964e8..676dd0034d5 100644 --- a/.eslintrc +++ b/.eslintrc @@ -2,7 +2,6 @@ "extends": [ "plugin:shopify/typescript", "plugin:shopify/react", - "plugin:shopify/eslint-comments", "plugin:shopify/jest", "plugin:shopify/node", "plugin:shopify/polaris", @@ -28,13 +27,16 @@ "allowBlockStart": false } ], + "babel/no-unused-expressions": "off", "import/named": "off", "import/no-named-as-default": "off", "react/button-has-type": "off", "react/no-array-index-key": "off", "react/jsx-fragments": ["error", "element"], "shopify/jsx-no-complex-expressions": "off", + "shopify/jsx-prefer-fragment-wrappers": "off", "shopify/no-ancestor-directory-import": "error", + "shopify/react-prefer-private-members": "off", "jsx-a11y/label-has-for": [ 2, { @@ -46,6 +48,7 @@ ], "jsx-a11y/no-autofocus": "off", "jsx-a11y/anchor-has-content": "off", + "jsx-a11y/control-has-associated-label": "off", "jsx-a11y/role-supports-aria-props": "off", "jsx-a11y/mouse-events-have-key-events": "off", "jsx-a11y/click-events-have-key-events": "off", @@ -56,6 +59,8 @@ { "files": ["**/*.test.{ts,tsx}"], "rules": { + "jest/no-truthy-falsy": "off", + "shopify/jsx-no-hardcoded-content": "off", "shopify/no-ancestor-directory-import": "off" } }, @@ -63,13 +68,15 @@ "files": ["examples/**/*.js"], "rules": { "import/no-extraneous-dependencies": "off", - "import/no-unresolved": "off" + "import/no-unresolved": "off", + "shopify/jsx-no-hardcoded-content": "off" } }, { "files": ["playground/Playground.tsx"], "rules": { "react/prefer-stateless-function": "off", + "shopify/jsx-no-hardcoded-content": "off", "shopify/react-initialize-state": "off" } } diff --git a/UNRELEASED.md b/UNRELEASED.md index 7f340b9bf65..71deaf9563d 100644 --- a/UNRELEASED.md +++ b/UNRELEASED.md @@ -60,7 +60,8 @@ Use [the changelog guidelines](https://git.io/polaris-changelog-guidelines) to f - Update most `devDependencies` ([#1327](https://github.com/Shopify/polaris-react/pull/1327)) - Bump `@shopify/react-utilites` to remove a transitive dependency on `core-js` ([#1343](https://github.com/Shopify/polaris-react/pull/1343)) - Updated App Bridge to version 1.3.0 ([#1349](https://github.com/Shopify/polaris-react/pull/1349)) -- Updated TypeScript to 3.2.4 ([#1388](https://github.com/Shopify/polaris-react/pull/1388)) +- Updated typescript to 3.2.4 ([#1388](https://github.com/Shopify/polaris-react/pull/1388)) +- Updated sewing-kit to 0.83.1 and babel-preset-shopify to ^18.1.0 ([#1344](https://github.com/Shopify/polaris-react/pull/1344)) ### Code quality diff --git a/babel.config.js b/babel.config.js index a8d561a7407..b1a4f5e3cbe 100644 --- a/babel.config.js +++ b/babel.config.js @@ -6,7 +6,7 @@ module.exports = function(api) { }); const runtimePreset = isWeb - ? ['babel-preset-shopify/web', {modules: false}] + ? ['babel-preset-shopify/web', {modules: false, useBuiltIns: 'entry'}] : ['babel-preset-shopify/node', {modules: 'commonjs'}]; // babel-preset-shopify/react only uses HMR if hot is true and the env is diff --git a/examples/browserify/src/App.js b/examples/browserify/src/App.js index f6c8ceeaa2b..aefef0b3655 100644 --- a/examples/browserify/src/App.js +++ b/examples/browserify/src/App.js @@ -36,6 +36,10 @@ class App extends Component { {label: 'I consent to receiving emails', value: 'false2'}, ]; + const accountMarkup = this.state.connected + ? this.disconnectAccountMarkup() + : this.connectAccountMarkup(); + return ( - {this.renderAccount()} + {accountMarkup} ); } - - renderAccount() { - return this.state.connected - ? this.disconnectAccountMarkup() - : this.connectAccountMarkup(); - } } export default App; diff --git a/examples/create-react-app/src/App.js b/examples/create-react-app/src/App.js index 393955010bd..876b5707c82 100644 --- a/examples/create-react-app/src/App.js +++ b/examples/create-react-app/src/App.js @@ -38,6 +38,10 @@ class App extends Component { {label: 'I consent to receiving emails', value: 'false2'}, ]; + const accountMarkup = this.state.connected + ? this.disconnectAccountMarkup() + : this.connectAccountMarkup(); + return ( - {this.renderAccount()} + {accountMarkup} ); } - - renderAccount() { - return this.state.connected - ? this.disconnectAccountMarkup() - : this.connectAccountMarkup(); - } } export default App; diff --git a/examples/webpack/src/App.js b/examples/webpack/src/App.js index 7a5a8d31e6c..e4166243e6f 100644 --- a/examples/webpack/src/App.js +++ b/examples/webpack/src/App.js @@ -36,6 +36,10 @@ class App extends Component { {label: 'I consent to receiving emails', value: 'false2'}, ]; + const accountMarkup = this.state.connected + ? this.disconnectAccountMarkup() + : this.connectAccountMarkup(); + return ( - {this.renderAccount()} + {accountMarkup} ); } - - renderAccount() { - return this.state.connected - ? this.disconnectAccountMarkup() - : this.connectAccountMarkup(); - } } export default App; diff --git a/package.json b/package.json index bc861817257..fee9ada878b 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "check:ci": "npm-run-all lint ts test:ci", "clean": "rimraf build esnext styles types docs \"build-intermediate\" \"index.*\" \"./src/styles/polaris-tokens\" \"styles.{css,scss}\"", "clean:build": "rimraf \"build/!(cache|coverage|storybook)\" esnext styles types docs \"build-intermediate\" \"index.*\" \"./src/styles/polaris-tokens\" \"styles.{css,scss}\"", - "optimize": "node ./scripts/optimize.js", + "optimize": "sewing-kit optimize", "prebuild": "npm-run-all clean:build optimize copy-polaris-tokens", "build": "node ./scripts/build.js", "prebuild-consumer": "yarn run build", @@ -91,11 +91,11 @@ "ios >= 9" ], "devDependencies": { - "@babel/core": "^7.1.6", + "@babel/core": "^7.4.3", "@percy/storybook": "^3.0.1", "@shopify/jest-dom-mocks": "^2.1.1", "@shopify/js-uploader": "github:shopify/js-uploader", - "@shopify/sewing-kit": "0.69.0", + "@shopify/sewing-kit": "0.83.1", "@storybook/addon-a11y": "^5.0.6", "@storybook/addon-actions": "^5.0.6", "@storybook/addon-backgrounds": "^5.0.6", @@ -112,7 +112,7 @@ "aws-sdk": "^2.58.0", "babel-core": "7.0.0-bridge.0", "babel-loader": "^8.0.5", - "babel-preset-shopify": "^17.0.1", + "babel-preset-shopify": "^18.1.1", "chalk": "^2.4.2", "change-case": "^3.1.0", "codecov": "^3.3.0", diff --git a/scripts/optimize.js b/scripts/optimize.js deleted file mode 100644 index ef3bffa1d87..00000000000 --- a/scripts/optimize.js +++ /dev/null @@ -1,37 +0,0 @@ -/* eslint-disable no-console */ - -const {resolve: resolvePath, basename, dirname} = require('path'); -const SVGO = require('svgo'); -const glob = require('glob'); -const {paramCase} = require('change-case'); -const {readFileSync, writeFileSync, removeSync} = require('fs-extra'); - -const {svgOptions} = require('@shopify/images/optimize'); - -const svgo = new SVGO(svgOptions()); - -glob(resolvePath(__dirname, '../src/**/*.svg'), (error, files) => { - if (error) { - console.error(error); - process.exit(1); - return; - } - - files.forEach((file) => optimizeFile(file)); -}); - -function optimizeFile(file) { - return new Promise((resolve) => { - const data = readFileSync(file, 'utf8'); - svgo.optimize(data, (result) => { - removeSync(file); - - const newFile = resolvePath( - dirname(file), - `${paramCase(basename(file, '.svg'))}.svg`, - ); - writeFileSync(newFile, `${result.data}\n`); - resolve(); - }); - }); -} diff --git a/src/components/AppProvider/utilities/Intl/tests/translate.test.ts b/src/components/AppProvider/utilities/Intl/tests/translate.test.ts index 18c1bf55ad0..6649e3c3ff5 100644 --- a/src/components/AppProvider/utilities/Intl/tests/translate.test.ts +++ b/src/components/AppProvider/utilities/Intl/tests/translate.test.ts @@ -36,7 +36,9 @@ describe('translate()', () => { it('throws an error for a missing replacement', () => { expect(() => translateWithReplacements('foo {next}', {notNext: 'bar'}), - ).toThrow(); + ).toThrow( + `No replacement found for key 'next'. The following replacements were passed: 'notNext'`, + ); }); }); }); diff --git a/src/components/AppProvider/utilities/createAppProviderContext/tests/createAppProviderContext.test.tsx b/src/components/AppProvider/utilities/createAppProviderContext/tests/createAppProviderContext.test.tsx index a846417873f..eadda91c612 100644 --- a/src/components/AppProvider/utilities/createAppProviderContext/tests/createAppProviderContext.test.tsx +++ b/src/components/AppProvider/utilities/createAppProviderContext/tests/createAppProviderContext.test.tsx @@ -107,8 +107,8 @@ describe('createAppProviderContext()', () => { }, }); - expect(Intl).toBeCalledWith(i18n); - expect(Link).toBeCalledWith(CustomLinkComponent); + expect(Intl).toHaveBeenCalledWith(i18n); + expect(Link).toHaveBeenCalledWith(CustomLinkComponent); }); it('adds an app bridge hook to set clientInterface data', () => { @@ -130,7 +130,7 @@ describe('createAppProviderContext()', () => { const next = jest.fn((args) => args); const baseAction = {type: 'actionType'}; - expect(setClientInterfaceHook.call({}, next)(baseAction)).toEqual({ + expect(setClientInterfaceHook.call({}, next)(baseAction)).toStrictEqual({ type: 'actionType', clientInterface: { name: '@shopify/polaris', diff --git a/src/components/AppProvider/utilities/createPolarisContext/tests/createPolarisContext.test.tsx b/src/components/AppProvider/utilities/createPolarisContext/tests/createPolarisContext.test.tsx index b74deb59515..354f8c4ce33 100644 --- a/src/components/AppProvider/utilities/createPolarisContext/tests/createPolarisContext.test.tsx +++ b/src/components/AppProvider/utilities/createPolarisContext/tests/createPolarisContext.test.tsx @@ -101,8 +101,8 @@ describe('createPolarisContext()', () => { }, }; - expect(contextOne).toEqual(mockContext); - expect(contextTwo).toEqual(mockContext); + expect(contextOne).toStrictEqual(mockContext); + expect(contextTwo).toStrictEqual(mockContext); }); it('returns the right context with only app provider context being provided', () => { @@ -140,7 +140,7 @@ describe('createPolarisContext()', () => { }, }; - expect(context).toEqual(mockContext); + expect(context).toStrictEqual(mockContext); }); it('returns the right context with only theme provider context being provided', () => { diff --git a/src/components/Autocomplete/components/ComboBox/tests/ComboBox.test.tsx b/src/components/Autocomplete/components/ComboBox/tests/ComboBox.test.tsx index 16358f056a6..cc1d343e0ce 100644 --- a/src/components/Autocomplete/components/ComboBox/tests/ComboBox.test.tsx +++ b/src/components/Autocomplete/components/ComboBox/tests/ComboBox.test.tsx @@ -221,7 +221,7 @@ describe('', () => { ); comboBox.simulate('click'); - expect(comboBox.find(OptionList).prop('selected')).toEqual([ + expect(comboBox.find(OptionList).prop('selected')).toStrictEqual([ 'cheese_pizza', ]); }); diff --git a/src/components/Autocomplete/tests/Autocomplete.test.tsx b/src/components/Autocomplete/tests/Autocomplete.test.tsx index 8e841e180c1..6ee8f508717 100644 --- a/src/components/Autocomplete/tests/Autocomplete.test.tsx +++ b/src/components/Autocomplete/tests/Autocomplete.test.tsx @@ -72,10 +72,10 @@ describe('', () => { expect(autocomplete.find(ComboBox).prop('id')).toBe('Autocomplete-ID'); expect(autocomplete.find(ComboBox).prop('options')).toBe(options); - expect(autocomplete.find(ComboBox).prop('selected')).toEqual([ + expect(autocomplete.find(ComboBox).prop('selected')).toStrictEqual([ 'cheese_pizza', ]); - expect(autocomplete.find(ComboBox).prop('textField')).toEqual( + expect(autocomplete.find(ComboBox).prop('textField')).toStrictEqual( renderTextField(), ); expect(autocomplete.find(ComboBox).prop('preferredPosition')).toBe( @@ -83,11 +83,11 @@ describe('', () => { ); expect(autocomplete.find(ComboBox).prop('listTitle')).toBe('List title'); expect(autocomplete.find(ComboBox).prop('allowMultiple')).toBe(true); - expect(autocomplete.find(ComboBox).prop('actionsBefore')).toEqual( + expect(autocomplete.find(ComboBox).prop('actionsBefore')).toStrictEqual( actionBefore, ); expect(autocomplete.find(ComboBox).prop('onSelect')).toBe(handleOnSelect); - expect(autocomplete.find(ComboBox).prop('emptyState')).toEqual( + expect(autocomplete.find(ComboBox).prop('emptyState')).toStrictEqual( , ); }); @@ -104,7 +104,7 @@ describe('', () => { loading />, ); - expect(autocomplete.find(ComboBox).prop('options')).toEqual([]); + expect(autocomplete.find(ComboBox).prop('options')).toStrictEqual([]); expect(autocomplete.find(ComboBox).prop('contentAfter')).not.toBeNull(); }); }); diff --git a/src/components/CalloutCard/tests/CalloutCard.test.tsx b/src/components/CalloutCard/tests/CalloutCard.test.tsx index d8f6efc7a5d..6f781891d25 100644 --- a/src/components/CalloutCard/tests/CalloutCard.test.tsx +++ b/src/components/CalloutCard/tests/CalloutCard.test.tsx @@ -59,6 +59,6 @@ describe('', () => { .first() .simulate('click'); - expect(spy).toBeCalled(); + expect(spy).toHaveBeenCalled(); }); }); diff --git a/src/components/Card/components/Header/tests/Header.test.tsx b/src/components/Card/components/Header/tests/Header.test.tsx index 92c556dc7f0..87a0029d7fd 100644 --- a/src/components/Card/components/Header/tests/Header.test.tsx +++ b/src/components/Card/components/Header/tests/Header.test.tsx @@ -41,7 +41,10 @@ describe('
', () => { it('renders buttons for each action', () => { mountWithAppProvider(
); - expect(buttonsFromMock).toBeCalledWith(mockActions, expect.anything()); + expect(buttonsFromMock).toHaveBeenCalledWith( + mockActions, + expect.anything(), + ); }); it('does not render a button group when not defined', () => { diff --git a/src/components/Card/components/Section/tests/Section.test.tsx b/src/components/Card/components/Section/tests/Section.test.tsx index a487ff591db..5dd05b4e88d 100644 --- a/src/components/Card/components/Section/tests/Section.test.tsx +++ b/src/components/Card/components/Section/tests/Section.test.tsx @@ -17,7 +17,7 @@ describe('', () => { const card = mountWithAppProvider(
); const headerMarkup = card.find('h2'); - expect(headerMarkup.text().includes(titleString)).toBe(true); + expect(headerMarkup.text()).toContain(titleString); expect(headerMarkup.find('Badge').text()).toBe(badgeString); }); @@ -28,6 +28,6 @@ describe('', () => { const headerMarkup = card.find(Subheading); expect(headerMarkup.exists()).toBeTruthy(); - expect(headerMarkup.text()).toEqual(titleString); + expect(headerMarkup.text()).toStrictEqual(titleString); }); }); diff --git a/src/components/Card/tests/Card.test.tsx b/src/components/Card/tests/Card.test.tsx index 9c0f35c1382..5d68e56240f 100644 --- a/src/components/Card/tests/Card.test.tsx +++ b/src/components/Card/tests/Card.test.tsx @@ -41,7 +41,7 @@ describe('', () => { const card = mountWithAppProvider(); const headerMarkup = card.find('h2'); - expect(headerMarkup.text().includes(titleString)).toBe(true); + expect(headerMarkup.text()).toContain(titleString); expect(headerMarkup.find('Badge').text()).toBe(badgeString); }); diff --git a/src/components/Checkbox/Checkbox.tsx b/src/components/Checkbox/Checkbox.tsx index 0c21248ec5c..8eb68c05e29 100644 --- a/src/components/Checkbox/Checkbox.tsx +++ b/src/components/Checkbox/Checkbox.tsx @@ -122,7 +122,7 @@ class Checkbox extends React.PureComponent { ); return ( - /* eslint-disable jsx-a11y/no-redundant-roles, jsx-a11y/role-has-required-aria-props */ + /* eslint-disable jsx-a11y/no-redundant-roles */ { - /* eslint-enable jsx-a11y/no-redundant-roles, jsx-a11y/role-has-required-aria-props */ + /* eslint-enable jsx-a11y/no-redundant-roles */ ); } } diff --git a/src/components/ChoiceList/tests/ChoiceList.test.tsx b/src/components/ChoiceList/tests/ChoiceList.test.tsx index 3a0ce4ff0a3..3d1bb1f8cc7 100644 --- a/src/components/ChoiceList/tests/ChoiceList.test.tsx +++ b/src/components/ChoiceList/tests/ChoiceList.test.tsx @@ -271,21 +271,15 @@ describe('', () => { const choiceElements = shallowWithAppProvider( , ).find(RadioButton); - let name: string; + const name = choiceElements.at(0).prop('name'); + expect(typeof name).toBe('string'); choiceElements.forEach((choiceElement) => { - const choiceName = choiceElement.prop('name'); - if (name == null) { - name = choiceName; - } else { - expect(choiceName).toBe(name); - } - - expect(typeof choiceName).toBe('string'); + expect(choiceElement.prop('name')).toBe(name); }); }); - it('uses the same name for every choice', () => { + it('uses the same name for choices', () => { const name = 'MyChoiceList'; const choiceElements = shallowWithAppProvider( , diff --git a/src/components/ColorPicker/tests/ColorPicker.test.tsx b/src/components/ColorPicker/tests/ColorPicker.test.tsx index b23cc4e2e23..8824c6f7a4b 100644 --- a/src/components/ColorPicker/tests/ColorPicker.test.tsx +++ b/src/components/ColorPicker/tests/ColorPicker.test.tsx @@ -79,7 +79,7 @@ describe('', () => { , ); - expect(colorPicker.find(AlphaPicker).prop('color')).toEqual(red); + expect(colorPicker.find(AlphaPicker).prop('color')).toStrictEqual(red); }); }); diff --git a/src/components/DataTable/tests/DataTable.test.tsx b/src/components/DataTable/tests/DataTable.test.tsx index 277c3c56b6a..83b80e7b7b3 100644 --- a/src/components/DataTable/tests/DataTable.test.tsx +++ b/src/components/DataTable/tests/DataTable.test.tsx @@ -105,13 +105,13 @@ describe('', () => { .find(Cell) .first() .prop('content'), - ).toEqual('Product'); + ).toStrictEqual('Product'); expect( dataTable .find(Cell) .first() .prop('contentType'), - ).toEqual('text'); + ).toStrictEqual('text'); }); }); @@ -179,7 +179,7 @@ describe('', () => { previousColumn: {leftEdge: 357, rightEdge: 474, isVisible: true}, currentColumn: {leftEdge: 474, rightEdge: 601, isVisible: true}, }; - expect(actualMeasurement).toEqual(expectedMeasurement); + expect(actualMeasurement).toStrictEqual(expectedMeasurement); }); }); }); diff --git a/src/components/DatePicker/DatePicker.tsx b/src/components/DatePicker/DatePicker.tsx index 63968a40a02..982a8737ef1 100644 --- a/src/components/DatePicker/DatePicker.tsx +++ b/src/components/DatePicker/DatePicker.tsx @@ -144,7 +144,6 @@ export class DatePicker extends React.PureComponent { showPreviousYear, }, )} - // eslint-disable-next-line react/jsx-no-bind onClick={this.handleMonthChangeClick.bind( null, showPreviousMonth, @@ -158,7 +157,6 @@ export class DatePicker extends React.PureComponent { nextMonth, nextYear, })} - // eslint-disable-next-line react/jsx-no-bind onClick={this.handleMonthChangeClick.bind( null, showNextMonth, diff --git a/src/components/DatePicker/components/Day/Day.tsx b/src/components/DatePicker/components/Day/Day.tsx index 2c9737e725e..432223e5664 100644 --- a/src/components/DatePicker/components/Day/Day.tsx +++ b/src/components/DatePicker/components/Day/Day.tsx @@ -68,14 +68,12 @@ export class Day extends React.PureComponent { return ( diff --git a/src/components/TopBar/components/Menu/tests/Menu.test.tsx b/src/components/TopBar/components/Menu/tests/Menu.test.tsx index c57699c3cba..453b8eeef5f 100644 --- a/src/components/TopBar/components/Menu/tests/Menu.test.tsx +++ b/src/components/TopBar/components/Menu/tests/Menu.test.tsx @@ -77,7 +77,7 @@ describe('', () => { , ); - expect(menu.find(Message).prop('badge')).toEqual(message.badge); + expect(menu.find(Message).prop('badge')).toStrictEqual(message.badge); }); it('calls the onClose prop in the Popover onClose', () => { diff --git a/src/components/TopBar/components/SearchField/SearchField.tsx b/src/components/TopBar/components/SearchField/SearchField.tsx index ff49a9c666b..f9fa5b3ba41 100644 --- a/src/components/TopBar/components/SearchField/SearchField.tsx +++ b/src/components/TopBar/components/SearchField/SearchField.tsx @@ -4,6 +4,7 @@ import {classNames} from '@shopify/react-utilities/styles'; import {noop} from '../../../../utilities/other'; import Icon from '../../../Icon'; +import {withAppProvider, WithAppProviderProps} from '../../../AppProvider'; import styles from './SearchField.scss'; @@ -26,7 +27,9 @@ export interface Props { onCancel?(): void; } -export default class SearchField extends React.Component { +export type ComposedProps = Props & WithAppProviderProps; + +export class SearchField extends React.Component { private input: React.RefObject = React.createRef(); componentDidMount() { @@ -58,12 +61,20 @@ export default class SearchField extends React.Component { } render() { - const {value, focused, active, placeholder} = this.props; + const { + value, + focused, + active, + placeholder, + polaris: {intl}, + } = this.props; const clearMarkup = value !== '' && (