
Research
/Security News
Weaponizing Discord for Command and Control Across npm, PyPI, and RubyGems.org
Socket researchers uncover how threat actors weaponize Discord across the npm, PyPI, and RubyGems ecosystems to exfiltrate sensitive data.
@cmpsr/components
Advanced tools
[](https://github.com/cmpsr/composer/actions/workflows/test.yml) [, unless you find that the counterpart does fit the specs (see an example).
It is required to add stories for all components, the storybook can be started by executing npm run storybook
.
npm run storybook
The implementation of a component will be divided in two parts, theming and implementation. Ideally when a new component is added to the library the main focus of the pull request will be on the theme and not in the implementation of the component.
All the UI customisation applied to a component must be defined inside the theme.
The theme of a component will be defined in a file with the name of the component inside the packages/components/src/theme/ComposerTheme/Components
folder. This is true for components that already exists in chakra and for new components added by us, like TextPairing. Once defined the component has to be re-exported from the index file.
textStyle
prop is not possible (chakra will not apply it to the component) and we have to apply the properties of the style one by one, in those cases use the functional option theme
param to apply the styles:export const Component: ComponentStyleConfig = {
sizes: {
xs: ({ theme }) => ({
...theme.textStyles["text-body-floating-label-medium"],
}),
},
};
export const Component: ComponentStyleConfig = {
sizes: {
xs: {
p: "1rem", // π
p: "16px", // π
p: 0, // π
p: "0", // π
p: "0rem", // π
p: "0px", // π
},
},
};
export const Component: ComponentStyleConfig = {
sizes: {
xs: {
borderRadius: "0.125rem", // π
px: "0.5rem", // π
py: "spacer-2", // π (unless specified in figma)
},
},
};
export const Card: ComponentStyleConfig = {
baseStyle: {
borderRadius: "radii-card", // π specified in figma
padding: "1.25rem", // π
},
};
baseStyle
instead:export const Component: ComponentStyleConfig = {
// π
variant: {
onlyVariant: {
borderRadius: "0.125rem",
},
},
};
export const Component: ComponentStyleConfig = {
// π
baseStyle: {
borderRadius: "0.125rem",
},
};
defaultProps
section in their props with the default variant
and or size
. If the default values are not defined in figma ping the design team in slack or add a comment in the pull request and will get those values for you.export const Component: ComponentStyleConfig = {
variants: {
primary: {},
secondary: {},
},
sizes: {
s: {},
m: {},
l: {},
}
defaultProps: {
variant: 'primary'
size: 'm',
},
};
The non-theme related code of the component will live in the packages/components/src/components
folder, inside a folder that defines the category of the component like form, layouts, navigation, etc. For each component we will create a new folder inside the corresponding category folder, if the category folder does not exists it should be created too, so a new form Component
will have to be defined in the packages/components/src/components/form/Component
folder for instance.
// packages/components/src/components/index.ts
export * from "./form";
export * from "./layouts";
// packages/components/src/components/layout/index.ts
export * from "./Box";
export * from "./Flex";
export * from "./Grid";
export * from "./Section";
For each component we will create 5 files. For more details on what to do and not to do in each continue reading.
types.ts
: types associated to the component (Props, Size, Variant, ...)Component.tsx
: component implementationComponent.test.tsx
: unit testsComponent.stories.tsx
: component storybookindex.ts
: re-export the component and the typesIn the types.ts
we will define all types associated for a component. As a bare minimum this file will contain the props that the component accepts. If a component is based on a chakra component its properties should extends its counterpart props. If a component has variants and or sizes we will create a custom type for those.
import { ComponentsProps as ChakraComponentProps } from "@chakra-ui/react";
// π
export const componentVariants = ["primary", "secondary"] as const;
export type ComponentVariant = (typeof componentVariants)[number];
export const componentSizes = ["xs", "s", "m", "l"] as const;
export type ComponentSize = (typeof componentSizes)[number];
export interface ComponentProps extends ChakraComponentProps {
variant?: ComponentVariant;
size?: ComponentSize;
}
// π prefix with the component name
export const variants = ["primary", "secondary"] as const;
export const Variant = typeof variants[number];
// π do not use first upper cased later for variables, only for types
export const ComponentVariants = ["primary", "secondary"] as const;
// π do not use plural for size or variant types
export const ComponentVariants = typeof componentVariants[number];
export const ComponentSizes = typeof componentSizes[number];
types.ts
export { FlexProps } from "@chakra-ui/react"; // π
// π Do not define a new _empty_ type
import { FlexProps as ChakraFlexProps } from "@chakra-ui/react";
export interface FlexProps extends ChakraFlexProps {}
In the Component.tsx
is where the actual implementation of the component will be. The goal is to have the minimum amount of code possible, i.e. only add logic to a chakra component if there are no other options.
All components has to be exported as forward references using the forwardRef
function exposed by chakra and not React.forwardRef
. This rule might not be followed in the examples shown in the documentation for simplicity.
export { forwardRef } from "@chakra-ui/react"; // π
export { forwardRef } from "react"; // π
useStyleConfig
(docs) or useMultiStyleConfig
(docs) and then apply the props:// π
const Component: FC<ComponentProps> = ({ size, ...rest }) => {
const styles = useStyleConfig("Component", { size }) as ComponentStyle;
return (
<ChakraComponent data-testid="cmpsr.component.id" {...styles} {...rest} />
);
};
// π
// The main issue with this approach is that if a user overrides our theme
// we will not apply the appropriate values.
//
// Our build script should detect this and fail
import { Component as Styles } from "@theme/ComposerTheme/Components/Component";
const Component: FC<ComponentProps> = ({ size, ...rest }) => {
const styles = Styles[size] as ComponentStyle;
return (
<ChakraComponent data-testid="cmpsr.component.id" {...styles} {...rest} />
);
};
id
added to a component should be prefixed with cmpsr.component-name.prop-name
, including data-testid
, as show in the previous snipped. And should be overridable.// π
const Component: FC<ComponentProps> = (props) => (
<ChakraComponent data-testid="cmpsr.component.data-testid" {...props} />
);
// π
const Component: FC<ComponentProps> = (props) => (
<ChakraComponent {...props} data-testid="cmpsr.component.data-testid" />
);
// π
export const Component: FC<ComponentProps> = (props) => {
const { childrenBg, p } = useStyleConfig("Component", props);
return (
<ChakraComponent p={p}>
<Children bg={childrenBg} />
</ChakraComponent>
);
};
// π€
// Doing this is not a red flag, but should be avoided
export const Component: FC<ComponentProps> = (props) => (
<ChakraComponent p="0.5rem" {...props}>
<Children bg="background-action-active" />
</ChakraComponent>
);
The Component.test.tsx
file is for unit testing the component. As we are relying on chakra-ui and we know their components are properly tested we will reduce the amount of unit tests added to a component, for example there is no reason to add a unit test to verify that the onClick
prop is called when a Button
is clicked. It is required to unit test any logic added to the component, but no UI changes as those will be verified by storybook+chromatic. If you find yourself mocking chakra-ui inside the unit test of a component you should consider this as a red flag. Testing variant
, size
and other theme props can be considered as a red flag too.
The storybook file Component.stories.tsx
must render all the variants and sizes of the component. The storybook should be clear and self-explanatory, ideally in a table format. All stories file must have a Playground
component where the user can interact with the component and test the main properties:
const Template = ({ showLeadingIcon, showTrailingIcon, ...args }) => (
<Button
{...(showLeadingIcon && { leadingIcon: Icons.IconExternalLink })}
{...(showTrailingIcon && { trailingIcon: Icons.IconExternalLink })}
{...args}
>
Playground
</Button>
);
export const Playground = Template.bind({});
Playground.args = {
variant: "primary",
size: "m",
children: "Composer button!",
isLoading: false,
showLeadingIcon: true,
showTrailingIcon: true,
isDisabled: false,
};
export default {
component: Component,
title: "Components/Form/Component",
argTypes: {
variant: {
options: componentVariants,
control: { type: "select" },
},
},
} as Meta;
index.ts
is responsible of re-exporting the component and the associated types:export * from "./Component";
export * from "./types"; // It is ok to only re-export ComponentProps
@/components
or @hooks
unless the imported file is in the same folder as your component. You can use the relative path import if you are going only going one level out:import { OtherComponent } from "@components"; // π
import { useResponsiveValue } from "@hooks"; // π
import { OtherComponent } from "."; // π
import { ComponentProps } from "./types"; // π
import { OtherComponent } from ".."; // π
import { OtherComponent } from "../OtherComponent"; // π
import { OtherComponent } from "../../OtherComponent"; // π
import { VStack } from "@chakra-ui/react"; // π
import { Button } from "@chakra-ui/react"; // π
We will favour using dot notation over composed component names.
// π
const Component = forwardRef<ComponentProps, 'div'>((props, ref) => (
<div ref={ref} {...props}/>
)
);
const Child = forwardRef<ComponentChildProps, 'div'>((props, ref) => (
<div ref={ref} {...props}/>
)
);
const ComponentNamespace = Object.assign(Component, { Child });
export { ComponentNamespace as Component };
// π
export Component = forwardRef<ComponentProps, 'div'>((props, ref) => (
<div ref={ref} {...props}/>
)
);
export const ComponentChild = forwardRef<ComponentChildProps, 'div'>((props, ref) => (
<div ref={ref} {...props}/>
)
);
You can take a look to the implementation of the Slider or Breadcrumb components for more details about how we do it.
There are a few cases of components that we will use literally as it from chakra (like Box or Flex). In those cases we will only add an index.ts
and a Component.stories.tsx
. The index file will just re-export the component and its props from chakra:
export { Box, BoxProps } from "@chakra-ui/react";
textStyle
to a multipart component, this will not always produce the desired effect. In general the textStyle
should be applied to the label
part.export const Component: ComponentStyleConfig = {
sizes: {
xs: ({ theme }) => ({
label: {
...theme.textStyles["text-body-floating-label-medium"],
}),
},
},
};
Composer icons are a subset of @tabler/icons-react
.
If a new version of tabler icons has been release and you just want to update the existing icons, then you only need to update the @tabler/icons-react
version in the package.json and you should be done.
But to be safe, run npm run gen:icons
to ensure that @tabler/icons-react
hasn't released a breaking change. No icons will be generated, but the script will throw if there is a breaking change.
If you wanna add or remove an icon, ensure to add or remove it from the icons list on the generate_icons.js file.
Once @tabler/icons-react
has been updated and all icons you want to keep are on the list, you can then run this command to generate the icons and their stories.
npm run gen:icons
All icon files will be generated and ready to commit. Just remember that if an icon has been removed or renamed that is a breaking change in Composer and you should update the Composer version accordingly.
If @tabler/icons-react
releases a breaking change, the generate_icons.js script may throw and it should be adjusted as needed.
Before creating the pull request you have to generate a changeset for your components, follow the instructions in here.
FAQs
[](https://github.com/cmpsr/composer/actions/workflows/test.yml) [![Chromatic Actions status](https://github.com/cmpsr/composer/actions/workflows
The npm package @cmpsr/components receives a total of 154 weekly downloads. As such, @cmpsr/components popularity was classified as not popular.
We found that @cmpsr/components demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago.Β It has 3 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
/Security News
Socket researchers uncover how threat actors weaponize Discord across the npm, PyPI, and RubyGems ecosystems to exfiltrate sensitive data.
Security News
Socket now integrates with Bun 1.3βs Security Scanner API to block risky packages at install time and enforce your organizationβs policies in local dev and CI.
Research
The Socket Threat Research Team is tracking weekly intrusions into the npm registry that follow a repeatable adversarial playbook used by North Korean state-sponsored actors.