diff --git a/CHANGELOG.md b/CHANGELOG.md index 8435fd5..50bf314 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,15 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [1.8.0] +## [1.10.0] + +### Added + +- Added `Segmented` component. +- Added `Alert.ErrorBoundary` component. +- Add `fixedSiderBreakpoint` prop for `DashboardTemplateSider`. + +## [1.9.0] ### Added diff --git a/package.json b/package.json index ae27e2a..f759307 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "1byte-react-design", - "version": "1.9.0", + "version": "1.10.0", "description": "A simple React UI library", "main": "dist/index.js", "module": "dist/index.js", diff --git a/src/molecules/Alert/Alert.tsx b/src/molecules/Alert/Alert.tsx index 5522cd3..c1bf97e 100644 --- a/src/molecules/Alert/Alert.tsx +++ b/src/molecules/Alert/Alert.tsx @@ -1,9 +1,12 @@ import { forwardRef } from 'react'; import { AlertStyled } from './styles'; import { RdAlertComponent, RdAlertCompoundedComponent } from './types'; +import { AlertErrorBoundary } from './ErrorBoundary'; export const InternalAlert: RdAlertComponent = forwardRef((props, ref) => { return ; }); export const Alert: RdAlertCompoundedComponent = InternalAlert as RdAlertCompoundedComponent; + +Alert.ErrorBoundary = AlertErrorBoundary; diff --git a/src/molecules/Alert/ErrorBoundary.tsx b/src/molecules/Alert/ErrorBoundary.tsx new file mode 100644 index 0000000..0983203 --- /dev/null +++ b/src/molecules/Alert/ErrorBoundary.tsx @@ -0,0 +1,33 @@ +import React from 'react'; +import { AlertErrorBoundaryStyled } from './styles'; +import { + RdAlertErrorBoundaryComponent, + RdAlertErrorBoundaryProps, + RdAlertErrorBoundaryStates, +} from './types'; + +export class AlertErrorBoundary + extends React.Component + implements RdAlertErrorBoundaryComponent +{ + state: RdAlertErrorBoundaryStates = { + error: undefined, + info: { + componentStack: '', + }, + }; + + componentDidCatch(error: Error | null, info: object): void { + // update state with error + stack trace from Antd + this.setState({ + error, + info: { + componentStack: (info as any).componentStack ?? '', + }, + }); + } + + render() { + return ; + } +} diff --git a/src/molecules/Alert/styles.tsx b/src/molecules/Alert/styles.tsx index df89122..ddfe420 100644 --- a/src/molecules/Alert/styles.tsx +++ b/src/molecules/Alert/styles.tsx @@ -1,4 +1,9 @@ import styled from '@emotion/styled'; import { Alert } from 'antd'; +import { RdAlertErrorBoundaryComponent } from './types'; export const AlertStyled = styled(Alert)``; + +export const AlertErrorBoundaryStyled = styled( + Alert.ErrorBoundary as typeof RdAlertErrorBoundaryComponent +)``; diff --git a/src/molecules/Alert/types.ts b/src/molecules/Alert/types.ts index 7cc0469..e1dcc04 100644 --- a/src/molecules/Alert/types.ts +++ b/src/molecules/Alert/types.ts @@ -1,11 +1,61 @@ import { Alert, GetProps } from 'antd'; import { AlertRef } from 'antd/es/alert/Alert'; import { ComponentToken as AlertComponentTokenAntd } from 'antd/es/drawer/style'; -import { ComponentProps } from 'react'; +import React, { ComponentProps } from 'react'; type AlertPropsAntd = GetProps; -type AlertErrorBoundaryPropsAntd = GetProps; +/** + * ๐Ÿ“ Explanation: + * + * For most Ant Design components, we can simply get the props type using: + * type XxxPropsAntd = GetProps; + * + * However, `Alert.ErrorBoundary` is a special case: + * - Ant Design does NOT export a public props interface for ErrorBoundary. + * - If we directly use `GetProps`, + * TypeScript will try to expose internal types and throw an error: + * ts(4023): "Exported variable '...' has or is using name '...' but cannot be named". + * + * ๐Ÿš‘ Workaround: + * - Use `ComponentProps` to get the props, + * then clone it via a mapped type to produce a "public" type. + * - This way we avoid referencing Ant Designโ€™s internal types. + * + * ๐Ÿ”ฎ Note: + * - If Ant Design exports a proper `AlertErrorBoundaryProps` type in the future, + * we can safely remove this workaround and write: + * type AlertErrorBoundaryPropsAntd = GetProps; + */ +type AlertErrorBoundaryPropsAntd = { + [K in keyof ComponentProps]: ComponentProps< + typeof Alert.ErrorBoundary + >[K]; +}; + +/** + * ๐Ÿ“ Explanation: + * + * Ant Design also does not export the `ErrorBoundaryStates` type. + * Therefore, instead of extracting it automatically, we need to define this interface manually. + * + * The state shape is referenced from the source code: + * state: { + * error: undefined; + * info: { + * componentStack: string; + * }; + * } + * + * This is simply a copied definition to ensure type safety for our extended components. + * If Ant Design exports `ErrorBoundaryStates` in the future, we should switch to that instead. + */ +interface AlertErrorBoundaryStatesAntd { + error?: Error | null; + info?: { + componentStack?: string; + }; +} type AlertRefAntd = AlertRef; //#endregion @@ -17,6 +67,7 @@ type AlertComponentTokenExtend = {}; //#region Define extended types type AlertPropsExtend = {}; type AlertErrorBoundaryPropsExtend = {}; +type AlertErrorBoundaryStatesExtend = {}; type AlertRefExtend = {}; //#endregion @@ -24,17 +75,26 @@ type AlertRefExtend = {}; //#region Export types export type RdAlertProps = AlertPropsAntd & AlertPropsExtend; export type RdAlertErrorBoundaryProps = AlertErrorBoundaryPropsAntd & AlertErrorBoundaryPropsExtend; +export type RdAlertErrorBoundaryStates = AlertErrorBoundaryStatesAntd & + AlertErrorBoundaryStatesExtend; export type RdAlertRef = AlertRefAntd & AlertRefExtend; export type RdAlertComponentToken = AlertComponentTokenAntd & AlertComponentTokenExtend; //#endregion //#region Define component types -// export type RdAlertComponent = React.FC; export type RdAlertComponent = React.ForwardRefExoticComponent< RdAlertProps & React.RefAttributes >; +export declare class RdAlertErrorBoundaryComponent extends React.Component< + RdAlertErrorBoundaryProps, + RdAlertErrorBoundaryStates +> { + state: RdAlertErrorBoundaryStates; + componentDidCatch(error: Error | null, info: object): void; + render(): React.ReactNode; +} export type RdAlertCompoundedComponent = RdAlertComponent & { - // Timer: RdAlertErrorBoundaryComponent; + ErrorBoundary: typeof RdAlertErrorBoundaryComponent; }; //#endregion diff --git a/src/molecules/Segmented/Segmented.tsx b/src/molecules/Segmented/Segmented.tsx new file mode 100644 index 0000000..8b8848c --- /dev/null +++ b/src/molecules/Segmented/Segmented.tsx @@ -0,0 +1,15 @@ +import { useMemo } from 'react'; +import { SegmentedStyledFunc } from './styles'; +import { RdSegmentedComponent, RdSegmentedProps } from './types'; + +export const InternalSegmented = ( + props: RdSegmentedProps +) => { + const SegmentedStyled = useMemo(() => { + return SegmentedStyledFunc(); + }, [SegmentedStyledFunc]); + + return ; +}; + +export const Segmented = InternalSegmented as RdSegmentedComponent; diff --git a/src/molecules/Segmented/index.tsx b/src/molecules/Segmented/index.tsx new file mode 100644 index 0000000..c59bd98 --- /dev/null +++ b/src/molecules/Segmented/index.tsx @@ -0,0 +1,2 @@ +export { Segmented } from './Segmented'; +export * from './types'; \ No newline at end of file diff --git a/src/molecules/Segmented/styles.tsx b/src/molecules/Segmented/styles.tsx new file mode 100644 index 0000000..b0a7365 --- /dev/null +++ b/src/molecules/Segmented/styles.tsx @@ -0,0 +1,7 @@ +import styled from '@emotion/styled'; +import { Segmented } from 'antd'; + +export const SegmentedStyles = styled(Segmented)``; + +export const SegmentedStyledFunc = () => + styled(Segmented)``; diff --git a/src/molecules/Segmented/types.ts b/src/molecules/Segmented/types.ts new file mode 100644 index 0000000..b5ef299 --- /dev/null +++ b/src/molecules/Segmented/types.ts @@ -0,0 +1,30 @@ +import { GetProps, Segmented } from 'antd'; +import { ComponentToken as SegmentedComponentTokenAntd } from 'antd/es/affix/style'; +import { InternalSegmented } from './Segmented'; + +//#region Define Ant Design types +type SegmentedPropsAntd = GetProps>; +//#endregion + +//#region Define extended component tokens +type SegmentedComponentTokenExtend = {}; +//#endregion + +//#region Define extended types +type SegmentedPropsExtend = {}; + +type SegmentedRefExtend = {}; +//#endregion + +//#region Export types +export type RdSegmentedProps = SegmentedPropsAntd & SegmentedPropsExtend; + +export type RdSegmentedComponentToken = SegmentedComponentTokenAntd & SegmentedComponentTokenExtend; +//#endregion + +//#region Define component types +export type RdSegmentedComponent = (( + props: RdSegmentedProps & React.RefAttributes +) => ReturnType) & + Pick; +//#endregion diff --git a/src/molecules/Select/types.ts b/src/molecules/Select/types.ts index d5d7240..17182d9 100644 --- a/src/molecules/Select/types.ts +++ b/src/molecules/Select/types.ts @@ -61,7 +61,18 @@ type SelectPropsExtend = { type SelectOptionPropsExtend = {}; type SelectOptionGroupPropsExtend = {}; type BaseOptionTypeExtend = {}; -type DefaultOptionTypeExtend = {}; +type DefaultOptionTypeExtend = { + /** + * Extends Ant Design's DefaultOptionType. + * + * Reason: + * - Ant Design's