|
| 1 | +import { Meta, Title } from '@storybook/blocks'; |
| 2 | + |
| 3 | +<Meta title="Migration guide" /> |
| 4 | +<Title>Migration guide</Title> |
| 5 | + |
| 6 | +When migrating a component from the 1st generation to the 2nd generation, there are a few things to keep in mind. |
| 7 | + |
| 8 | +## Component structure |
| 9 | + |
| 10 | +Components in 1st and 2nd generation share a common base class. These base classes are located in the `second-gen/packages/core/components/` directory. |
| 11 | + |
| 12 | +- 1st generation components are located in the `first-gen/packages/components/` directory. |
| 13 | +- 2nd generation components are located in the `second-gen/packages/swc/components/` directory. |
| 14 | + |
| 15 | +Keep in mind that all the code in the core package is shared between the 1st and 2nd generation components and should avoid any breaking changes being introduced here that could alter the 1st generation API. |
| 16 | + |
| 17 | +Relevant files in the 1st generation components: |
| 18 | + |
| 19 | +- `src/<component-name>-overrides.css` - Theme switching system; do not delete this file, even if it is empty |
| 20 | +- `src/<component-name>.css` - Component styles; this file is used to import the styles from the Spectrum CSS project and the component styles and adds web component-specific styles |
| 21 | +- `src/spectrum-<component-name>.css` - Styles imported and transformed by the build process from the Spectrum CSS project |
| 22 | +- `src/<component-name>.ts` - Component implementation; this contains the attributes and properties for the component as well as the rendering logic |
| 23 | +- `stories/` - Stories |
| 24 | +- `test/` - Tests |
| 25 | +- `README.md` - Component documentation |
| 26 | + |
| 27 | +Relevant files in the 2nd generation components: |
| 28 | + |
| 29 | +- `<component-name>.css` - Component styles; all styles should be added to this file |
| 30 | +- `<component-name>.ts` - Component implementation; this contains the attributes and properties for the component as well as the rendering logic |
| 31 | +- `stories/` - Stories will also serve as the documentation for the component; the story file should be named `<component-name>.stories.ts`. Use JSDoc to document a story in more detail. |
| 32 | +- `test/` - Tests |
| 33 | + |
| 34 | +## Getting started |
| 35 | + |
| 36 | +### Create the base class |
| 37 | + |
| 38 | +If the component folder does not yet exist in the core package, you will need to create it. It might be easiest to copy the existing component file from the 1st generation component and remove the render and styling logic as well as anything that is S1-specific. |
| 39 | + |
| 40 | +For example, if you are migrating the `sp-accordion` component, you can copy the `first-gen/packages/accordion/src/Accordion.ts` to `second-gen/packages/core/components/accordion/Accordion.base.ts` and remove the render and styling logic as well as anything that is S1-specific. |
| 41 | + |
| 42 | +### Implement the component class |
| 43 | + |
| 44 | +Once the base class is created, you can start to implement the component in the `second-gen/packages/swc/components/` directory. The implementation file should be named `<component-name>.ts` and should extend the base class. |
| 45 | + |
| 46 | +```ts |
| 47 | +import { AccordionBase } from '@swc/core/components/accordion'; |
| 48 | + |
| 49 | +export class Accordion extends AccordionBase { |
| 50 | + // ... |
| 51 | +} |
| 52 | +``` |
| 53 | + |
| 54 | +### Bring over styles from Spectrum CSS |
| 55 | + |
| 56 | +Now we can bring over styles from the `spectrum-two` package in Spectrum CSS. Start by checking out the `@adobe/spectrum-css` repository and pulling down the `spectrum-two` branch. |
| 57 | + |
| 58 | +Identify the component you need to migrate by searching the `components` directory for the component name. |
| 59 | + |
| 60 | +Inside the CSS directory, you can expect to find the following files: |
| 61 | + |
| 62 | +- `index.css` - This is the source of truth for the component Styles |
| 63 | +- `dist/index.css` - This is the processed version of the component styles; for now, this is the best place to source styles for SWC. When copying files from Spectrum CSS into the 2nd-generation component, using this file will ensure you get the benefits of the CSS build tooling. |
| 64 | +- `stories/<component-name>.stories.js` - This is a great source-of-truth for the SWC storybook. These stories are organized into logic groups with API defined in a customer-friendly fashion with typing and human-readable labels. These files also include migration notes and guidance specific to the S2-specific implementation of the component. |
| 65 | +- `stories/template.js` - This is a great source-of-truth for the SWC render function; these templates already include property and class mappings. When bringing this file over to SWC, be sure to remove the `id` and `customStyles` attributes as they do not translate to the web component APIs. |
| 66 | + |
| 67 | +Next, we need to copy the styles from the Spectrum CSS component to the 2nd-generation component. |
| 68 | + |
| 69 | +```bash |
| 70 | +cp -r spectrum-css/components/<component-name>/dist/index.css spectrum-web-components/second-gen/packages/swc/components/<component-name>/<component-name>.css |
| 71 | +``` |
| 72 | + |
| 73 | +### Update styles in the 2nd-generation component |
| 74 | + |
| 75 | +Now that we have the base styles in place, we need to check the first-gen implementation for any unique web component-specific styles that would not exist in the vanilla CSS implementation. This information will most likely be found in the `first-gen/packages/components/<component-name>/<component-name>.css` file. |
| 76 | + |
| 77 | +For example, look for styles specific to slots, such as `::slotted([name="icon"]) {}`. |
| 78 | + |
| 79 | +If these styles are found, we need to confirm if they are needed in the 2nd-generation component. Not all first-gen overrides or component-specific styles are needed in the 2nd-generation components and sometimes there are other ways to source those styles using the original classes provided by the Spectrum CSS asset. |
| 80 | + |
| 81 | +It might be helpful, at this point, to define the render function for the second-gen component so you can spin up Storybook and start seeing these new styles in action. A quick way to kick this off is to copy the `spectrum-css/components/<component-name>/stories/template.js` file into a render function on your new 2nd-generation component. |
| 82 | + |
| 83 | +Let's use the `Badge` component as an example. First, we need to add the styles to the component. |
| 84 | + |
| 85 | +Start by importing the CSS file: |
| 86 | + |
| 87 | +```ts |
| 88 | +import styles from './badge.css'; |
| 89 | +``` |
| 90 | + |
| 91 | +Next, we import those styles into the component: |
| 92 | + |
| 93 | +```ts |
| 94 | +public static override get styles(): CSSResultArray { |
| 95 | + return [styles]; |
| 96 | +} |
| 97 | +``` |
| 98 | + |
| 99 | +Finally, we can start to implement the render function: |
| 100 | + |
| 101 | +```ts |
| 102 | +protected override render(): TemplateResult { |
| 103 | + return html` |
| 104 | + <div |
| 105 | + class=${classMap({ |
| 106 | + ['spectrum-Badge']: true, |
| 107 | + [`spectrum-Badge--size${this.size?.toUpperCase()}`]: typeof this.size !== 'undefined', |
| 108 | + [`spectrum-Badge--${this.variant}`]: typeof this.variant !== 'undefined', |
| 109 | + [`spectrum-Badge--subtle`]: this.subtle, |
| 110 | + [`spectrum-Badge--outline`]: this.outline, |
| 111 | + [`spectrum-Badge--fixed-${this.fixed}`]: typeof this.fixed !== 'undefined', |
| 112 | + })} |
| 113 | + > |
| 114 | + ${when( |
| 115 | + this.hasIcon, |
| 116 | + () => html` |
| 117 | + <div |
| 118 | + class=${classMap({ |
| 119 | + [`spectrum-Badge-icon`]: true, |
| 120 | + [`spectrum-Badge-icon--no-label`]: |
| 121 | + !this.slotHasContent, |
| 122 | + })} |
| 123 | + > |
| 124 | + <slot name="icon"></slot> |
| 125 | + </div> |
| 126 | + ` |
| 127 | + )} |
| 128 | + <div class="spectrum-Badge-label"> |
| 129 | + <slot></slot> |
| 130 | + </div> |
| 131 | + </div> |
| 132 | + `; |
| 133 | +} |
| 134 | +``` |
| 135 | + |
| 136 | +Let's compare this to the 1st-generation implementation: |
| 137 | + |
| 138 | +```ts |
| 139 | +protected override render(): TemplateResult { |
| 140 | + return html` |
| 141 | + ${this.hasIcon |
| 142 | + ? html` |
| 143 | + <slot |
| 144 | + name="icon" |
| 145 | + ?icon-only=${!this.slotHasContent} |
| 146 | + ></slot> |
| 147 | + ` |
| 148 | + : nothing} |
| 149 | + <div class="label"> |
| 150 | + <slot></slot> |
| 151 | + </div> |
| 152 | + `; |
| 153 | +} |
| 154 | +``` |
| 155 | + |
| 156 | +As you can see, the 2nd-generation implementation leverages the `classMap` function to conditionally apply classes to the component based on the component's properties. This is a common pattern in the 2nd-generation components. This approach has several benefits: |
| 157 | + |
| 158 | +- It separates the styling application from the properties and states of the component |
| 159 | +- It creates a container inside the Shadow DOM which provides stronger encapsulation |
| 160 | +- It allows for more efficient rendering by only applying the necessary classes to the component |
| 161 | + |
| 162 | +In our 2nd-generation version, we will likely want to maintain any slots available in the 1st-generation component unless design changes from S2 provide a compelling reason to change or remove them. |
| 163 | + |
| 164 | +Once your render function is in place, you can spin up Storybook and start seeing these new styles in action. |
| 165 | + |
| 166 | +```bash |
| 167 | +yarn workspace @swc/components storybook |
| 168 | +``` |
| 169 | + |
| 170 | +_Note_: Run all commands provided in this guide from the root of the Spectrum Web Components monorepo. |
| 171 | + |
| 172 | +_Tip_: If you want to run S1 Storybook alongside S2 Storybook, you can use the following command: |
| 173 | + |
| 174 | +```bash |
| 175 | +yarn start |
| 176 | +``` |
| 177 | + |
| 178 | +## Implementing the API |
| 179 | + |
| 180 | +Now that we have the base styles and render function in place, we can start to adjust styling and API to match the 2nd-generation design language. |
| 181 | + |
| 182 | +Start by determining what display property your component's host should use. All components should have at least the following style added to the top of their 2nd-generation CSS. |
| 183 | + |
| 184 | +```css |
| 185 | +:host { |
| 186 | + display: inline-flex; |
| 187 | +} |
| 188 | +``` |
| 189 | + |
| 190 | +Use the `display` property most aligned with the component's semantic role or purpose in the document. |
| 191 | + |
| 192 | +### Properties |
| 193 | + |
| 194 | +The 2nd-generation components may have changes to the properties and attributes of the component. These changes are typically due to design changes from S2 or to align with the 2nd-generation design language. |
| 195 | + |
| 196 | +There are several resources you can use to help you understand the design changes from S2. |
| 197 | + |
| 198 | +- [Migration roadmap](https://github.com/adobe/spectrum-web-components/tree/2nd-gen-component-analysis/migration-roadmap) - SWC branch name: `2nd-gen-component-analysis` |
| 199 | +- [S2 design assets](https://www.figma.com/design/Mngz9H7WZLbrCvGQf3GnsY/S2---Desktop?m=auto) - Figma file |
| 200 | + |
| 201 | +You can also use the `CHANGELOG.md` file in the Spectrum CSS repository to see the migration notes for the component. |
| 202 | + |
| 203 | +```bash |
| 204 | +cat spectrum-css/components/<component-name>/CHANGELOG.md |
| 205 | +``` |
0 commit comments