From 4d6f1e13ea1c5ad2a5a250e9436e0d15a3684a7a Mon Sep 17 00:00:00 2001 From: Josh Black Date: Mon, 13 May 2019 22:26:35 -0500 Subject: [PATCH 1/4] docs(publishing): update internal publishing docs (#2698) * docs(publishing): update internal publishing docs * Update publishing.md --- docs/publishing.md | 63 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/docs/publishing.md b/docs/publishing.md index 97fdc607419f..3df3938f33fc 100644 --- a/docs/publishing.md +++ b/docs/publishing.md @@ -7,6 +7,7 @@ - [Pre-release](#pre-release) - [Release](#release) +- [Publishing older library versions](#publishing-older-library-versions) - [FAQ](#faq) - [How do I fix the repo state if I cancel during a publish?](#how-do-i-fix-the-repo-state-if-i-cancel-during-a-publish) @@ -38,6 +39,68 @@ 8. Run `./tasks/publish.sh ---exact --conventional-commits --github-release --git-remote upstream` +## Publishing older library versions + +We offer ad-hoc backwards-support for older version of the system. This work is +primarly driven by external contributors who may still need these older versions +for legacy code. When updates are received and merged into the codebase, the +release process will be a bit different than the one described above. + +For example, with +[`carbon-components-react`](https://github.com/carbon-design-system/carbon-components-react) +we have specific branches for older major versions like `v5` or `v6`. If we +wanted to publish an update to either of these major versions, this process +would look like: + +- Checkout the branch locally, making sure to pull in the latest from upstream +- Manually update `package.json` with the new version to publish in a branch + called `chore/release-vX.Y.Z` and a commit message: `chore(release): vX.Y.Z` +- Create a Pull Request with this new branch and commit message +- Once this is merged into the branch, checkout locally and pull latest. Now we + can publish by running `npm publish .`, if you want to do a dry run first you + can do `npm publish . --dry-run`. This is helpful when dependencies may be + different than in newer versions of the system + +One important thing to verify is that `package.json` has a `publishConfig` field +that looks like the following: + +```json +{ + "publishConfig": { + "tag": ".x" + } +} +``` + +For example, `carbon-components-react` v5 would look like: + +```json +{ + "publishConfig": { + "tag": "5.x" + } +} +``` + +This tag verifies that when we publish we do not publish to the `latest` tag but +instead to the major-specific tag for the package. + +After running `npm publish .` and seeing the package publish to the registry, +you could create a git tag by running: + +```bash +git tag -a vX.Y.Z # The commit message should match vX.Y.Z +``` + +You should then push this tag to the project by running: + +```bash +git push upstream vX.Y.Z +``` + +This helps keep track of what versions have been published and snapshotting the +code at that point in time. + ## FAQ #### How do I fix the repo state if I cancel during a publish? From 868c5710a4d034626b1c6695b827244eabc02682 Mon Sep 17 00:00:00 2001 From: Callum Smith <1744185+cal-smith@users.noreply.github.com> Date: Tue, 14 May 2019 00:51:33 -0400 Subject: [PATCH 2/4] fix(dropdown): set z-index to above the modal (#2660) --- packages/components/src/globals/scss/_layout.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/components/src/globals/scss/_layout.scss b/packages/components/src/globals/scss/_layout.scss index 39c30d47b85b..f06a300d3d24 100644 --- a/packages/components/src/globals/scss/_layout.scss +++ b/packages/components/src/globals/scss/_layout.scss @@ -13,7 +13,7 @@ $z-indexes: ( modal: 9000, overlay: 8000, - dropdown: 7000, + dropdown: 9100, header: 6000, footer: 5000, hidden: - 1, From a1eb0fa0754f0039e3e9af5910e322f53681be60 Mon Sep 17 00:00:00 2001 From: Alessandro De Blasis Date: Tue, 14 May 2019 11:13:21 +0100 Subject: [PATCH 3/4] feat(react/css-prefix): removed hardcoded css-prefix (#2690) Replaced all the hardcoded instances of `bx--` with a prefix variable defined in carbon-components but that can be overridden for custom or whitelabeled scenarios. Updated storybook following the pattern described in: `examples/custom-css-prefix` --- packages/react/.storybook/Container.js | 4 +- packages/react/.storybook/_container.scss | 1 + packages/react/.storybook/settings.js | 4 + packages/react/.storybook/webpack.config.js | 24 +++++ .../components/Accordion/Accordion-test.js | 13 +-- .../AccordionItem/AccordionItem-test.js | 39 ++++++--- .../Breadcrumb/__tests__/Breadcrumb-test.js | 5 +- .../src/components/Button/Button-test.js | 29 ++++--- .../src/components/Checkbox/Checkbox-story.js | 11 ++- .../src/components/Checkbox/Checkbox-test.js | 11 ++- .../CodeSnippet/CodeSnippet-story.js | 2 +- .../CodeSnippet/CodeSnippet-test.js | 15 ++-- .../src/components/ComboBox/ComboBox-test.js | 5 +- .../ComposedModal/ComposedModal-story.js | 7 +- .../ComposedModal/ComposedModal-test.js | 43 +++++---- .../ContentSwitcher/ContentSwitcher-test.js | 5 +- .../react/src/components/Copy/Copy-test.js | 20 +++-- .../components/CopyButton/CopyButton-test.js | 23 ++--- .../DangerButton/DangerButton-test.js | 5 +- .../DataTable/__tests__/Table-test.js | 7 +- .../__tests__/TableBatchActions-test.js | 7 +- .../DataTableSkeleton-test.js | 19 ++-- .../components/DatePicker/DatePicker-test.js | 81 +++++++++++------ .../src/components/Dropdown/Dropdown-test.js | 21 +++-- .../FileUploader/FileUploader-story.js | 4 +- .../FileUploader/FileUploader-test.js | 23 +++-- .../react/src/components/Form/Form-test.js | 5 +- .../components/FormGroup/FormGroup-test.js | 5 +- .../InlineLoading/InlineLoading-test.js | 29 +++++-- .../react/src/components/Link/Link-test.js | 5 +- .../ListBox/__tests__/ListBox-test.js | 9 +- .../src/components/ListBox/test-helpers.js | 18 ++-- .../src/components/ListItem/ListItem-test.js | 5 +- .../src/components/Loading/Loading-test.js | 21 +++-- .../react/src/components/Modal/Modal-story.js | 5 +- .../react/src/components/Modal/Modal-test.js | 57 +++++++----- .../ModalWrapper/ModalWrapper-story.js | 4 +- .../ModalWrapper/ModalWrapper-test.js | 11 ++- .../MultiSelect/__tests__/MultiSelect-test.js | 87 +++++++++++-------- .../Notification/Notification-test.js | 49 +++++++---- .../NumberInput/NumberInput-test.js | 50 ++++++----- .../OrderedList/OrderedList-test.js | 9 +- .../OverflowMenu/OverflowMenu-test.js | 19 ++-- .../OverflowMenuItem/OverflowMenuItem-test.js | 10 ++- .../components/Pagination/Pagination-test.js | 77 ++++++++-------- .../PrimaryButton/PrimaryButton-test.js | 5 +- .../ProgressIndicator-story.js | 9 +- .../ProgressIndicator-test.js | 23 +++-- .../RadioButton/RadioButton-test.js | 23 +++-- .../RadioButtonGroup/RadioButtonGroup-test.js | 9 +- .../components/RadioTile/RadioTile-test.js | 17 ++-- .../src/components/Search/Search-test.js | 17 ++-- .../SearchFilterButton-test.js | 5 +- .../SearchLayoutButton-test.js | 5 +- .../SecondaryButton/SecondaryButton-test.js | 5 +- .../src/components/Select/Select-test.js | 24 ++--- .../components/SelectItem/SelectItem-test.js | 5 +- .../SelectItemGroup/SelectItemGroup-test.js | 4 +- .../SkeletonPlaceholder-test.js | 5 +- .../SkeletonText/SkeletonText-test.js | 7 +- .../src/components/Slider/Slider-test.js | 22 +++-- .../StructuredList/StructuredList-story.js | 5 +- .../StructuredList/StructuredList-test.js | 51 +++++++---- .../src/components/Switch/Switch-test.js | 15 ++-- packages/react/src/components/Tab/Tab-test.js | 19 ++-- .../react/src/components/Tabs/Tabs-test.js | 23 +++-- packages/react/src/components/Tag/Tag-test.js | 11 ++- .../src/components/TextArea/TextArea-test.js | 13 +-- .../TextInput/ControlledPasswordInput-test.js | 17 ++-- .../TextInput/ControlledPasswordInput.js | 2 +- .../TextInput/PasswordInput-test.js | 17 ++-- .../src/components/TextInput/PasswordInput.js | 2 +- .../components/TextInput/TextInput-test.js | 17 ++-- .../react/src/components/Tile/Tile-test.js | 29 ++++--- .../components/TimePicker/TimePicker-test.js | 9 +- .../src/components/Toggle/Toggle-test.js | 22 +++-- .../ToggleSmall/ToggleSmall-test.js | 18 ++-- .../src/components/Toolbar/Toolbar-test.js | 11 ++- .../src/components/Tooltip/Tooltip-test.js | 13 +-- .../UIShell/__tests__/HeaderMenu-test.js | 13 ++- .../UnorderedList/UnorderedList-test.js | 8 +- 81 files changed, 894 insertions(+), 509 deletions(-) create mode 100644 packages/react/.storybook/settings.js diff --git a/packages/react/.storybook/Container.js b/packages/react/.storybook/Container.js index 18b11df7d3c0..92479895e3ea 100644 --- a/packages/react/.storybook/Container.js +++ b/packages/react/.storybook/Container.js @@ -1,7 +1,9 @@ import React, { Component } from 'react'; import './polyfills'; import './_container.scss'; +import { settings } from 'carbon-components'; +const { prefix } = settings; export default class Container extends Component { componentDidMount() { if (process.env.CARBON_REACT_STORYBOOK_USE_RTL === 'true') { @@ -28,7 +30,7 @@ export default class Container extends Component { ); diff --git a/packages/react/.storybook/_container.scss b/packages/react/.storybook/_container.scss index a61ac81c70de..a00d94d2747e 100644 --- a/packages/react/.storybook/_container.scss +++ b/packages/react/.storybook/_container.scss @@ -1,4 +1,5 @@ $css--font-face: true; $css--reset: true; +$prefix: 'bx'; @import '~carbon-components/src/globals/scss/styles.scss'; diff --git a/packages/react/.storybook/settings.js b/packages/react/.storybook/settings.js new file mode 100644 index 000000000000..d6d2f9c707dc --- /dev/null +++ b/packages/react/.storybook/settings.js @@ -0,0 +1,4 @@ +const settings = { + prefix: 'bx', +}; +module.exports = settings; diff --git a/packages/react/.storybook/webpack.config.js b/packages/react/.storybook/webpack.config.js index e3aa10cfdc5b..73a0d62424f5 100644 --- a/packages/react/.storybook/webpack.config.js +++ b/packages/react/.storybook/webpack.config.js @@ -45,6 +45,25 @@ const styleLoaders = [ }, ]; +class FeatureFlagProxyPlugin { + /** + * A WebPack resolver plugin that proxies module request + * for `carbon-components/es/globals/js/settings` to `./settings`. + */ + constructor() { + this.source = 'before-described-relative'; + } + + apply(resolver) { + resolver.plugin(this.source, (request, callback) => { + if (/[\\/]globals[\\/]js[\\/]settings$/.test(request.path)) { + request.path = path.resolve(__dirname, './settings'); + } + callback(); + }); + } +} + module.exports = (baseConfig, env, defaultConfig) => { defaultConfig.devtool = useStyleSourceMap ? 'source-map' : ''; defaultConfig.optimization = { @@ -96,5 +115,10 @@ module.exports = (baseConfig, env, defaultConfig) => { ); } + defaultConfig.resolve = { + modules: ['node_modules'], + plugins: [new FeatureFlagProxyPlugin()], + }; + return defaultConfig; }; diff --git a/packages/react/src/components/Accordion/Accordion-test.js b/packages/react/src/components/Accordion/Accordion-test.js index 713fa86d670b..04fe39f63f83 100644 --- a/packages/react/src/components/Accordion/Accordion-test.js +++ b/packages/react/src/components/Accordion/Accordion-test.js @@ -10,6 +10,9 @@ import Accordion from '../Accordion'; import AccordionSkeleton from '../Accordion/Accordion.Skeleton'; import SkeletonText from '../SkeletonText'; import { shallow, mount } from 'enzyme'; +import { settings } from 'carbon-components'; + +const { prefix } = settings; describe('Accordion', () => { describe('Renders as expected', () => { @@ -24,7 +27,7 @@ describe('Accordion', () => { }); it('has the expected classes', () => { - expect(wrapper.hasClass('bx--accordion')).toEqual(true); + expect(wrapper.hasClass(`${prefix}--accordion`)).toEqual(true); }); it('renders extra classes passed in via className', () => { @@ -38,8 +41,8 @@ describe('AccordionSkeleton', () => { const wrapper = shallow(); it('Has the expected classes', () => { - expect(wrapper.hasClass('bx--skeleton')).toEqual(true); - expect(wrapper.hasClass('bx--accordion')).toEqual(true); + expect(wrapper.hasClass(`${prefix}--skeleton`)).toEqual(true); + expect(wrapper.hasClass(`${prefix}--accordion`)).toEqual(true); }); it('Renders first item as expected', () => { @@ -55,12 +58,12 @@ describe('AccordionSkeleton', () => { it('Renders number of items as expected', () => { const fullWrapper = mount(); - expect(fullWrapper.find('.bx--accordion__item')).toHaveLength(4); + expect(fullWrapper.find(`.${prefix}--accordion__item`)).toHaveLength(4); }); it('Renders custom number of items', () => { const fullWrapper = mount(); - expect(fullWrapper.find('.bx--accordion__item')).toHaveLength(8); + expect(fullWrapper.find(`.${prefix}--accordion__item`)).toHaveLength(8); }); }); }); diff --git a/packages/react/src/components/AccordionItem/AccordionItem-test.js b/packages/react/src/components/AccordionItem/AccordionItem-test.js index 926c11aa12e0..8c26699b3414 100644 --- a/packages/react/src/components/AccordionItem/AccordionItem-test.js +++ b/packages/react/src/components/AccordionItem/AccordionItem-test.js @@ -9,6 +9,9 @@ import React from 'react'; import AccordionItem from '../AccordionItem'; import ChevronRight16 from '@carbon/icons-react/lib/chevron--right/16'; import { shallow, mount } from 'enzyme'; +import { settings } from 'carbon-components'; + +const { prefix } = settings; describe('AccordionItem', () => { describe('Renders as expected', () => { @@ -19,27 +22,31 @@ describe('AccordionItem', () => { ); it('renders children as expected', () => { - expect(wrapper.find('.bx--accordion__content').text()).toBe( + expect(wrapper.find(`.${prefix}--accordion__content`).text()).toBe( 'Lorem ipsum.' ); }); it('renders heading as expected', () => { - const heading = wrapper.find('.bx--accordion__heading'); + const heading = wrapper.find(`.${prefix}--accordion__heading`); const icon = ChevronRight16; expect(heading.length).toBe(1); expect(heading.find(icon).length).toBe(1); - expect(heading.find('.bx--accordion__title').text()).toBe('A heading'); + expect(heading.find(`.${prefix}--accordion__title`).text()).toBe( + 'A heading' + ); }); it('should use correct icon', () => { - const heading = wrapper.find('.bx--accordion__heading'); + const heading = wrapper.find(`.${prefix}--accordion__heading`); expect(heading.find(ChevronRight16).length).toBe(1); }); it('has the expected classes', () => { - expect(wrapper.hasClass('bx--accordion__item')).toEqual(true); - expect(wrapper.hasClass('bx--accordion__item--active')).toEqual(false); + expect(wrapper.hasClass(`${prefix}--accordion__item`)).toEqual(true); + expect(wrapper.hasClass(`${prefix}--accordion__item--active`)).toEqual( + false + ); }); it('renders extra classes passed in via className', () => { @@ -52,7 +59,9 @@ describe('AccordionItem', () => { Lorem ipsum. ); - expect(openItem.hasClass('bx--accordion__item--active')).toEqual(true); + expect(openItem.hasClass(`${prefix}--accordion__item--active`)).toEqual( + true + ); expect(openItem.state().open).toEqual(true); openItem.setState({ open: true }); openItem.setProps({ open: false }); @@ -73,10 +82,12 @@ describe('AccordionItem', () => { it('should apply the active class when the state is open', () => { const toggler = mount(); const item = toggler.find('li'); - expect(item.hasClass('bx--accordion__item--active')).toEqual(false); + expect(item.hasClass(`${prefix}--accordion__item--active`)).toEqual( + false + ); toggler.setState({ open: true }); expect( - toggler.find('li').hasClass('bx--accordion__item--active') + toggler.find('li').hasClass(`${prefix}--accordion__item--active`) ).toEqual(true); }); }); @@ -94,10 +105,10 @@ describe('AccordionItem', () => { ); it('renders heading as expected', () => { - const heading = wrapper.find('.bx--accordion__heading'); + const heading = wrapper.find(`.${prefix}--accordion__heading`); expect(heading.length).toBe(1); expect(heading.find(ChevronRight16).length).toBe(1); - const title = heading.find('.bx--accordion__title'); + const title = heading.find(`.${prefix}--accordion__title`); expect(title.text()).toBe('A heading'); expect(title.find('h2').exists()).toEqual(true); expect(title.find('h2').hasClass('TitleClass')).toEqual(true); @@ -115,7 +126,7 @@ describe('AccordionItem', () => { const wrapper = mount( ); - const heading = wrapper.find('button.bx--accordion__heading'); + const heading = wrapper.find(`button.${prefix}--accordion__heading`); it('should call onClick', () => { wrapper.simulate('click'); @@ -132,7 +143,7 @@ describe('AccordionItem', () => { const toggler = mount( Lorem ipsum. ); - const heading = toggler.find('button.bx--accordion__heading'); + const heading = toggler.find(`button.${prefix}--accordion__heading`); it('should set state to open when clicked', () => { expect(toggler.state().open).toEqual(false); @@ -152,7 +163,7 @@ describe('AccordionItem', () => { ); - heading = toggler.find('button.bx--accordion__heading'); + heading = toggler.find(`button.${prefix}--accordion__heading`); }); it('should close open AccordionItem when using Esc', () => { diff --git a/packages/react/src/components/Breadcrumb/__tests__/Breadcrumb-test.js b/packages/react/src/components/Breadcrumb/__tests__/Breadcrumb-test.js index f1b727a4eb6c..82691c9ad85f 100644 --- a/packages/react/src/components/Breadcrumb/__tests__/Breadcrumb-test.js +++ b/packages/react/src/components/Breadcrumb/__tests__/Breadcrumb-test.js @@ -7,6 +7,9 @@ import React from 'react'; import { mount } from 'enzyme'; +import { settings } from 'carbon-components'; + +const { prefix } = settings; describe('Breadcrumb', () => { let Breadcrumb; @@ -63,7 +66,7 @@ describe('Breadcrumb', () => { expect(CustomComponent).toHaveBeenCalled(); expect(CustomComponent).toHaveBeenCalledWith( expect.objectContaining({ - className: 'bx--link', + className: `${prefix}--link`, }), {} ); diff --git a/packages/react/src/components/Button/Button-test.js b/packages/react/src/components/Button/Button-test.js index f524c118494b..e582ba985879 100644 --- a/packages/react/src/components/Button/Button-test.js +++ b/packages/react/src/components/Button/Button-test.js @@ -11,6 +11,9 @@ import Button from '../Button'; import Link from '../Link'; import ButtonSkeleton from '../Button/Button.Skeleton'; import { shallow, mount } from 'enzyme'; +import { settings } from 'carbon-components'; + +const { prefix } = settings; describe('Button', () => { describe('Renders common props as expected', () => { @@ -123,7 +126,7 @@ describe('Button', () => { const icon = iconButton.find('svg'); it('should have the appropriate icon', () => { - expect(icon.hasClass('bx--btn__icon')).toBe(true); + expect(icon.hasClass(`${prefix}--btn__icon`)).toBe(true); }); it('should return error if icon given without description', () => { @@ -148,7 +151,7 @@ describe('Button', () => { const icon = iconButton.find('svg'); it('should have the appropriate icon', () => { - expect(icon.hasClass('bx--btn__icon')).toBe(true); + expect(icon.hasClass(`${prefix}--btn__icon`)).toBe(true); expect(icon.find(':not(svg):not(title)').html()).toBe( originalIcon.children().html() ); @@ -172,7 +175,7 @@ describe('Primary Button', () => { const wrapper = shallow(