Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

COM-394: Automatically generate className for component slots with createComponentSlot() #1809

Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,10 @@ E.g.: in this instance of `MyComponent`, the root should have a red border, and
See this as a working example in our [Storybook](https://storybook.comet-dxp.com/?path=/story/comet-admin-theming--themable-mycomponent).

```tsx
import { ThemedComponentBaseProps } from "@comet/admin";
import { ThemedComponentBaseProps, createSlot } from "@comet/admin";
import { CometColor } from "@comet/admin-icons";
import { ComponentsOverrides, Typography } from "@mui/material";
import { css, styled, Theme, useThemeProps } from "@mui/material/styles";
import { css, Theme, useThemeProps } from "@mui/material/styles";
import React from "react";

/**
Expand All @@ -87,22 +87,21 @@ type OwnerState = Pick<MyComponentProps, "shadow">;

/**
* Each element or sub-component of a Comet Admin component should be created as a "slot".
* A slot is created by using the `styled` function from `@mui/material/styles` and passing in the HTML element or component you want to be the base of your slot.
* A slot is created by using the `createSlot` function and passing in the HTML element or component you want to be the base of your slot.
* The slot's options require:
* - `name`: The name of the component, prefixed by `CometAdmin`.
* This name is used to reference the component in the theme, so it must be the same for every slot in the component.
* - `slot`: The name of the slot. This name is used to reference the slot when overriding component styles or default-props.
* - `overridesResolver`: The function that applies the styles from the theme's styleOverrides to the slot.
* This should always return the styles of the slot's name, e.g. `styles.root` for the Root slot.
* If the slot has modifier styles, e.g. `styles.hasShadow`, then those should be returned as well when the modifier prop is set to `true`.
* - `componentName`: This is used to reference the component in the theme, so it must be the same for every slot in the component. It will be automatically prefixed with `CometAdmin`.
* - `slotName`: This name is used to reference the slot when overriding component styles or default-props in the theme, or when passing in slotProps to the component.
* - `classesResolver`: This function determines which class-key's `styleOverrides` and `className` to apply to the slot.
* The class-key of the `slotName`, e.g., `root` is always added to the slot automatically.
* If a slot has styles dependent on a value in the `ownerState`, e.g. from a `shadow` prop, then a class-key (e.g. `hasShadow`) should be returned to allow those styles to be overridden using the theme.
*/
const Root = styled("div", {
name: "CometAdminMyComponent",
slot: "root",
overridesResolver({ ownerState }: { ownerState: OwnerState }, styles) {
return [styles.root, ownerState.shadow && styles.hasShadow];
const Root = createSlot("div")<MyComponentClassKey, OwnerState>({
componentName: "MyComponent",
slotName: "root",
classesResolver(ownerState) {
return [ownerState.shadow && "hasShadow"];
},
})<{ ownerState: OwnerState }>(
})(
({ theme, ownerState }) => css`
background-color: ${theme.palette.background.paper};

Expand All @@ -113,12 +112,9 @@ const Root = styled("div", {
`,
);

const Header = styled("div", {
name: "CometAdminMyComponent",
slot: "header",
overridesResolver(_, styles) {
return [styles.header];
},
const Header = createSlot("div")<MyComponentClassKey>({
componentName: "MyComponent",
slotName: "header",
})(
({ theme }) => css`
display: flex;
Expand All @@ -128,13 +124,10 @@ const Header = styled("div", {
`,
);

const Title = styled(Typography, {
name: "CometAdminMyComponent",
slot: "title",
overridesResolver(_, styles) {
return [styles.title];
},
})<{ ownerState: OwnerState }>(
const Title = createSlot(Typography)<MyComponentClassKey, OwnerState>({
componentName: "MyComponent",
slotName: "title",
})(
({ ownerState }) => css`
${ownerState.shadow &&
css`
Expand All @@ -143,20 +136,14 @@ const Title = styled(Typography, {
`,
);

const Icon = styled(CometColor, {
name: "CometAdminMyComponent",
slot: "icon",
overridesResolver(_, styles) {
return [styles.icon];
},
const Icon = createSlot(CometColor)<MyComponentClassKey>({
componentName: "MyComponent",
slotName: "icon",
})();

const Children = styled("div", {
name: "CometAdminMyComponent",
slot: "children",
overridesResolver(_, styles) {
return [styles.children];
},
const Children = createSlot("div")<MyComponentClassKey>({
componentName: "MyComponent",
slotName: "children",
})(
({ theme }) => css`
padding: ${theme.spacing(4)};
Expand Down
176 changes: 70 additions & 106 deletions packages/admin/admin-color-picker/src/ColorPicker.slots.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { InputWithPopper, ThemedComponentBaseProps } from "@comet/admin";
import { createSlot, InputWithPopper, ThemedComponentBaseProps } from "@comet/admin";
import { Box, ButtonBase, IconButton, InputAdornment as MuiInputAdornment, Typography } from "@mui/material";
import { css, styled, Theme } from "@mui/material/styles";
import { css, Theme } from "@mui/material/styles";
import { HexColorPicker as HexColorPickerBase, RgbaStringColorPicker as RgbaStringColorPickerBase } from "react-colorful";

export type ColorPickerClassKey =
Expand Down Expand Up @@ -53,38 +53,26 @@ const getPopperSectionStyles = (theme: Theme) => css`
}
`;

export const Root = styled(InputWithPopper, {
name: "CometAdminColorPicker",
slot: "root",
overridesResolver(_, styles) {
return [styles.root];
},
})(css``);
export const Root = createSlot(InputWithPopper)<ColorPickerClassKey>({
componentName: "ColorPicker",
slotName: "root",
})();

export const InputAdornment = styled(MuiInputAdornment, {
name: "CometAdminColorPicker",
slot: "inputAdornment",
overridesResolver(_, styles) {
return [styles.inputAdornment];
},
})(css``);
export const InputAdornment = createSlot(MuiInputAdornment)<ColorPickerClassKey>({
componentName: "ColorPicker",
slotName: "inputAdornment",
})();

export const PopperRoot = styled("div", {
name: "CometAdminColorPicker",
slot: "popperRoot",
overridesResolver(_, styles) {
return [styles.popperRoot];
},
export const PopperRoot = createSlot("div")<ColorPickerClassKey>({
componentName: "ColorPicker",
slotName: "popperRoot",
})(css`
width: 300px;
`);

export const Header = styled("div", {
name: "CometAdminColorPicker",
slot: "header",
overridesResolver(_, styles) {
return [styles.header, styles.popperSection];
},
export const Header = createSlot("div")<ColorPickerClassKey>({
componentName: "ColorPicker",
slotName: "header",
})(
({ theme }) => css`
${getPopperSectionStyles(theme)}
Expand All @@ -93,24 +81,18 @@ export const Header = styled("div", {
`,
);

export const HeaderTitleText = styled(Typography, {
name: "CometAdminColorPicker",
slot: "headerTitleText",
overridesResolver(_, styles) {
return [styles.headerTitleText];
},
export const HeaderTitleText = createSlot(Typography)<ColorPickerClassKey>({
componentName: "ColorPicker",
slotName: "headerTitleText",
})(
({ theme }) => css`
font-weight: ${theme.typography.fontWeightBold};
`,
);

export const HeaderCloseButton = styled(IconButton, {
name: "CometAdminColorPicker",
slot: "headerCloseButton",
overridesResolver(_, styles) {
return [styles.headerCloseButton];
},
export const HeaderCloseButton = createSlot(IconButton)<ColorPickerClassKey>({
componentName: "ColorPicker",
slotName: "headerCloseButton",
})(
({ theme }) => css`
position: absolute;
Expand All @@ -120,11 +102,11 @@ export const HeaderCloseButton = styled(IconButton, {
`,
);

export const ColorPickerWrapper = styled("div", {
name: "CometAdminColorPicker",
slot: "colorPickerWrapper",
overridesResolver(_, styles) {
return [styles.colorPickerWrapper, styles.popperSection];
export const ColorPickerWrapper = createSlot("div")<ColorPickerClassKey>({
componentName: "ColorPicker",
slotName: "colorPickerWrapper",
classesResolver() {
return ["popperSection"];
},
})(
({ theme }) => css`
Expand Down Expand Up @@ -166,11 +148,11 @@ export const ColorPickerWrapper = styled("div", {
`,
);

export const ColorPalette = styled("div", {
name: "CometAdminColorPicker",
slot: "colorPalette",
overridesResolver(_, styles) {
return [styles.colorPalette, styles.popperSection];
export const ColorPalette = createSlot("div")<ColorPickerClassKey>({
componentName: "ColorPicker",
slotName: "colorPalette",
classesResolver() {
return ["popperSection"];
},
})(
({ theme }) => css`
Expand All @@ -185,31 +167,27 @@ type ColorPaletteItemProps = {
colorValue: string;
};

export const ColorPaletteItem = styled(Box, {
name: "CometAdminColorPicker",
slot: "colorPaletteItem",
shouldForwardProp: (prop) => prop !== "color",
overridesResolver(_, styles) {
return [styles.colorPaletteItem];
},
})<ColorPaletteItemProps>(
({ theme, colorValue }) => css`
export const ColorPaletteItem = createSlot(Box)<ColorPickerClassKey, ColorPaletteItemProps>({
componentName: "ColorPicker",
slotName: "colorPaletteItem",
})(
({ theme, ownerState }) => css`
cursor: pointer;
width: 24px;
height: 24px;
flex-shrink: 0;
border: thin solid ${theme.palette.grey[100]};
border-radius: ${theme.shape.borderRadius};
box-sizing: border-box;
background-color: ${colorValue};
background-color: ${ownerState.colorValue};
`,
);

export const Footer = styled("div", {
name: "CometAdminColorPicker",
slot: "footer",
overridesResolver(_, styles) {
return [styles.footer, styles.popperSection];
export const Footer = createSlot("div")<ColorPickerClassKey>({
componentName: "ColorPicker",
slotName: "footer",
classesResolver() {
return ["popperSection"];
},
})(
({ theme }) => css`
Expand All @@ -218,25 +196,19 @@ export const Footer = styled("div", {
`,
);

export const FooterClearButton = styled(ButtonBase, {
name: "CometAdminColorPicker",
slot: "footerClearButton",
overridesResolver(_, styles) {
return [styles.footerClearButton];
},
export const FooterClearButton = createSlot(ButtonBase)<ColorPickerClassKey>({
componentName: "ColorPicker",
slotName: "footerClearButton",
})(
({ theme }) => css`
padding: ${theme.spacing(2)};
border-radius: ${theme.shape.borderRadius};
`,
);

export const Preview = styled("div", {
name: "CometAdminColorPicker",
slot: "preview",
overridesResolver(_, styles) {
return [styles.preview];
},
export const Preview = createSlot("div")<ColorPickerClassKey>({
componentName: "ColorPicker",
slotName: "preview",
})(css`
position: relative;
overflow: hidden;
Expand All @@ -255,20 +227,18 @@ export type PreviewIndicatorColorProps = {
color: string;
};

export const PreviewIndicator = styled("div", {
name: "CometAdminColorPicker",
slot: "previewIndicator",
shouldForwardProp: (prop) => prop !== "type" && prop !== "color",
overridesResolver({ type }: PreviewIndicatorProps, styles) {
export const PreviewIndicator = createSlot("div")<ColorPickerClassKey, PreviewIndicatorProps>({
componentName: "ColorPicker",
slotName: "previewIndicator",
classesResolver({ type }) {
return [
styles.previewIndicator,
type === "color" && styles.previewIndicatorColor,
type === "empty" && styles.previewIndicatorEmpty,
type === "invalid" && styles.previewIndicatorInvalid,
type === "color" && "previewIndicatorColor",
type === "empty" && "previewIndicatorEmpty",
type === "invalid" && "previewIndicatorInvalid",
];
},
})<PreviewIndicatorProps>(
({ type, color, theme }) => css`
})(
({ theme, ownerState }) => css`
position: absolute;
left: 0;
right: 0;
Expand All @@ -277,12 +247,12 @@ export const PreviewIndicator = styled("div", {
border: thin solid ${theme.palette.divider};
border-radius: ${theme.shape.borderRadius};

${type === "color" &&
${ownerState.type === "color" &&
css`
background-color: ${color};
background-color: ${ownerState.color};
`}

${type === "empty" &&
${ownerState.type === "empty" &&
css`
display: flex;
align-items: center;
Expand All @@ -298,7 +268,7 @@ export const PreviewIndicator = styled("div", {
}
`}

${type === "invalid" &&
${ownerState.type === "invalid" &&
css`
font-size: 16px;
line-height: 24px;
Expand All @@ -309,18 +279,12 @@ export const PreviewIndicator = styled("div", {
`,
);

export const HexColorPicker = styled(HexColorPickerBase, {
name: "CometAdminColorPicker",
slot: "hexColorPicker",
overridesResolver(_, styles) {
return [styles.hexColorPicker];
},
})(css``);
export const HexColorPicker = createSlot(HexColorPickerBase)<ColorPickerClassKey>({
componentName: "ColorPicker",
slotName: "hexColorPicker",
})();

export const RgbaStringColorPicker = styled(RgbaStringColorPickerBase, {
name: "CometAdminColorPicker",
slot: "rgbaStringColorPicker",
overridesResolver(_, styles) {
return [styles.rgbaStringColorPicker];
},
})(css``);
export const RgbaStringColorPicker = createSlot(RgbaStringColorPickerBase)<ColorPickerClassKey>({
componentName: "ColorPicker",
slotName: "rgbaStringColorPicker",
})();
Loading
Loading