elastic / eui

Elastic UI Framework 🙌
https://eui.elastic.co/
Other
6.07k stars 821 forks source link

[Meta][CSS-in-JS] Component conversions #5685

Open thompsongl opened 2 years ago

thompsongl commented 2 years ago

Wiki Doc for How to Write Styles with Emotion

Completed - [x] [Accordion](https://elastic.github.io/eui/#/layout/accordion) (#5826) @1Copenut - [x] [Bottom bar](https://elastic.github.io/eui/#/layout/bottom-bar) @breehall #5823 - [x] [Flex](https://elastic.github.io/eui/#/layout/flex) @constancecchen #6270 - [x] [Flyout](https://elastic.github.io/eui/#/layout/flyout) @breehall #6213 - [x] [Horizontal rule](https://elastic.github.io/eui/#/layout/horizontal-rule) (#5815) - [x] [Page](https://elastic.github.io/eui/#/layout/page) @cchaos - [x] [Page header](https://elastic.github.io/eui/#/layout/page-header) @cchaos - [x] [Panel](https://elastic.github.io/eui/#/layout/panel) @cchaos #5891 - [x] [Popover](https://elastic.github.io/eui/#/layout/popover) @cchaos #5977 - [x] [Spacer](https://elastic.github.io/eui/#/layout/spacer) (#5812) - [x] [Button](https://elastic.github.io/eui/#/navigation/button) - #5989 - #6150 - [x] [Facet](https://elastic.github.io/eui/#/navigation/facet) @miukimiu (#5878) - [x] [Link](https://elastic.github.io/eui/#/navigation/link) (#5856) @MichaelMarcialis - [x] [Pagination](https://elastic.github.io/eui/#/navigation/pagination) #6109 - [x] [Tabs](https://elastic.github.io/eui/#/navigation/tabs) #6311 - [x] [Aspect ratio](https://elastic.github.io/eui/#/display/aspect-ratio) (#5818) - [x] [Avatar](https://elastic.github.io/eui/#/display/avatar) (#5670) - [x] [Badge](https://elastic.github.io/eui/#/display/badge) (#6224, #6258) - [x] [Callout](https://elastic.github.io/eui/#/display/callout) @cchaos #5870 - [x] [Card](https://elastic.github.io/eui/#/display/card) #6110 - [x] [Comment list](https://elastic.github.io/eui/#/display/comment-list) @miukimiu (#5692) - [x] [Description list](https://elastic.github.io/eui/#/display/description-list) @breehall #5971 - [x] [Health](https://elastic.github.io/eui/#/display/health) @constancecchen #5832 - [x] [Icons](https://elastic.github.io/eui/#/display/icons) @miukimiu #5967 - [x] [Token](https://elastic.github.io/eui/#/display/icons#tokens) @miukimiu #6067 - [x] [Image](https://elastic.github.io/eui/#/display/image) @miukimiu #5969 - [x] [List group](https://elastic.github.io/eui/#/display/list-group) @miukimiu #6207 - [x] [Loading](https://elastic.github.io/eui/#/display/loading) @cchaos #5845 - [x] [Loading Chart](https://github.com/elastic/eui/blob/aff98287c04a8fae6b6137c25633ede1f10378d7/src/components/loading/_loading_chart.scss) (#5821) @cchaos - [x] [Progress](https://elastic.github.io/eui/#/display/progress) @constancecchen #5986 - [x] [Range sliders](https://elastic.github.io/eui/#/forms/range-sliders) @miukimiu #6092 - [x] [Stat](https://elastic.github.io/eui/#/display/stat) @constancecchen #5968 - [x] [Text](https://elastic.github.io/eui/#/display/text) @constancecchen #5895 - [x] [Title](https://elastic.github.io/eui/#/display/title) @constancecchen #5842 - [x] [Toast](https://elastic.github.io/eui/#/display/toast) #6068 - [x] [Tooltip](https://elastic.github.io/eui/#/display/tooltip) #6104 - [x] [Tour](https://elastic.github.io/eui/#/display/tour) #6087 - [x] [Code](https://elastic.github.io/eui/#/editors-syntax/code) @miukimiu #6263 - [x] #6403 @miukimiu (#6321) **Utilities** - [x] [Accessibility](https://elastic.github.io/eui/#/utilities/accessibility) - [x] [Screen reader only](https://elastic.github.io/eui/#/utilities/accessibility#screen-reader-only) #5846 - [x] [Skip link](https://elastic.github.io/eui/#/utilities/accessibility#skip-link) #5851 - [x] [Beacon](https://elastic.github.io/eui/#/utilities/beacon) @miukimiu (#5814) - [x] [CSS utility classes](https://elastic.github.io/eui/#/utilities/css-utility-classes) #6124 - [x] [Error boundary](https://elastic.github.io/eui/#/utilities/error-boundary) #6053 - [x] [Highlight and mark](https://elastic.github.io/eui/#/utilities/highlight-and-mark) (#4575) - [x] [Overlay mask](https://elastic.github.io/eui/#/utilities/overlay-mask) #6090 - [x] [Portal](https://elastic.github.io/eui/#/utilities/portal) #6075 - [x] [Text diff](https://elastic.github.io/eui/#/utilities/text-diff) #6056
## Partially complete
- [x] https://github.com/elastic/eui/issues/6388
- [ ] https://github.com/elastic/eui/issues/6653
- [ ] #6386 
- [ ] #6408
- [ ] https://github.com/elastic/eui/issues/6406
## Display
- [ ] #6404
- [ ] #6397
- [ ] #6396
## Navigation
- [ ] #6389
- [ ] #6392
- [ ] #6393
- [ ] #6412
- [ ] #6416
- [ ] #6401
- [x] #6413
- [x] #6418
## Layout
- [ ] #6417
- [ ] https://github.com/elastic/eui/issues/6405
## Tables
- [ ] #6415 
- [ ] #6387
## Forms
- [x] https://github.com/elastic/eui/issues/6398
- [ ] https://github.com/elastic/eui/issues/6410
- [ ] https://github.com/elastic/eui/issues/6411
- [ ] https://github.com/elastic/eui/issues/6391
- [ ] https://github.com/elastic/eui/issues/6390
- [ ] #6400
## Low priority (3rd party components or components that may move out of EUI)
- [ ] #6395
- [ ] #6402 
- [ ] #6394 
- [ ] #6414
## Docs
- [ ] Guide page
- [ ] Guide rule
- [ ] Guide section
breehall commented 2 years ago

Component Conversion Process

Converting the Sass styles to Emotion

1. New file structure

Example

//imports
import { useEuiTheme } from '../../services';
import { euiAvatarStyles } from './avatar.styles';

//set the theme and configure Emotion styling
const euiTheme = useEuiTheme();
const styles = euiAvatarStyles(euiTheme);

2a. Convert & Remove Sass component styles

2b. Update style props types by removing classNameMaps where possible

See https://github.com/elastic/eui/pull/5889 for examples.

If the modifier piece of the class name --{modifier} is the same string as the possible prop values, switch to applying the modifier piece of the class name directly as they key. Remove the manual maps of property value to class name maps and change the array of values as an exported array as const.

Example:

const sizeToClassNameMap = {
  s: 'euiAvatar--s',
  m: 'euiAvatar--m',
  l: 'euiAvatar--l',
  xl: 'euiAvatar--xl',
};

export const SIZES = keysOf(sizeToClassNameMap);
export type EuiAvatarSize = keyof typeof sizeToClassNameMap;

const classes = classNames(
  'euiAvatar',
  sizeToClassNameMap[size],
);

Becomes:

export const SIZES = ['s', 'm', 'l', 'xl'] as const;
export type EuiAvatarSize = typeof SIZES[number];

const classes = classNames(
  'euiAvatar',
  {
    [`euiAvatar--${size}`]: size,
  },
);

If they don't match completely 1:1, you will need to keep the map, but remove the euiComponentName-- part of the value and still apply the className directly in the function:

export const MARGINS = ['none', 'xs', 's', 'm', 'l', 'xl', 'xxl'] as const;
export type EuiHorizontalRuleMargin = typeof MARGINS[number];

const marginToClassNameMap: {
  [value in EuiHorizontalRuleMargin]: string | null;
} = {
  none: null,
  xs: 'marginXSmall',
  ...
  xxl: 'marginXXLarge',
};

const classes = classNames(
  'euiHorizontalRule',
  {
    [`euiHorizontalRule--${marginToClassNameMap[margin]}`]: margin && margin !== 'none',
  },
);

2c. Removing vs. keeping classNames

In the above scenarios, removing the -- modifier classNames/maps may be a perfectly valid choice as well to clean up our classNames logic. Be sure to search through Kibana first for the modifier(s) classes:

  1. If no frequent Kibana usages come up (e.g. in CSS overrides or test selectors) outside of snapshots, fantastic! Go ahead and remove the classes.
  2. If there are minor usages, consider replacing the modifier class with a [data] attribute instead that consumers can target instead of a class.

NOTE: Generally, do not remove the top level className (e.g. .euiComponent) or child element classNames (e.g. .euiComponent__child). These classes serve as useful hooks/markers for consumers to use (both for CSS overrides and test selectors).

3. Move and remove style Amsterdam overrides (if present)

Check src/themes/amsterdam/overrides for component style overrides. If overrides are present:

4. Update component tests

Example

import { shouldRenderCustomStyles } from '../../test/internal';

describe('EuiAvatar', () => {
  test('it renders', () => { ... });

  shouldRenderCustomStyles(<EuiAvatar name="name" />);

  // more tests
}

5. Remove styling variables used by the EUI documentation site (if present)

6. Deprecate any component-specific mixins

Example if the recommendation is to use the actual React component

@mixin componentMixin(){
  ...
  @warn 'componentMixin() has been deprecated. Wrap your usage with <EuiComponentName /> instead.';
}

7. Update the documentation site playground (if using withEuiTheme HOC)

-  const docgenInfo = Array.isArray(EuiAccordion.__docgenInfo)
-    ? EuiAccordion.__docgenInfo[0]
-    : EuiAccordion.__docgenInfo;
-  const propsToUse = propUtilityForPlayground(docgenInfo.props);
+  const docgenInfo = Array.isArray(EuiAccordionClass.__docgenInfo)
+    ? EuiAccordionClass.__docgenInfo[0]
+    : EuiAccordionClass.__docgenInfo;
+  const propsToUse = propUtilityForPlayground(docgenInfo.props, true);

8. Changelog

Be sure to add the following header:

**CSS-in-JS conversions**

- Converted ___ to Emotion

If any Sass variables or mixins were removed, append them to the line item use a semi-colon separator

- Converted __ to Emotion; Removed `$___`
cchaos commented 2 years ago

EDIT: We've moved this conversion checklist to our GitHub PR template instead. See https://github.com/elastic/eui/pull/6294

cchaos commented 2 years ago

Do/When:

If a prop/value pair maps 1:1 to the CSS property: value, pass the value straight through

position?: CSSProperties['position'];

const cssStyles = [
  { position }
];

If a prop's value renders no styles

A. If it's necessary to still know the prop value while debugging, create an empty css`` map for that value

paddingSize = 'none';

const euiComponentStyles = ({
  none: css``
})

B. If it's mostly just an empty default state, check for that prop before grabbing the css value

paddingSize = 'none';

const cssStyles = [
  paddingSize === 'none' ? undefined : styles[paddingSize]
]