matrix.tsx 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. import {type ElementType, isValidElement} from 'react';
  2. import styled from '@emotion/styled';
  3. import JSXProperty from 'sentry/components/stories/jsxProperty';
  4. import type {Props as SizingWindowProps} from 'sentry/components/stories/sizingWindow';
  5. import SizingWindow from 'sentry/components/stories/sizingWindow';
  6. import {space} from 'sentry/styles/space';
  7. type RenderProps = {};
  8. export type PropMatrix<P extends RenderProps> = Partial<{
  9. [Prop in keyof P]: P[Prop][];
  10. }>;
  11. interface Props<P extends RenderProps> {
  12. propMatrix: PropMatrix<P>;
  13. render: ElementType<P>;
  14. selectedProps: [keyof P] | [keyof P, keyof P];
  15. sizingWindowProps?: SizingWindowProps;
  16. }
  17. export default function Matrix<P extends RenderProps>({
  18. propMatrix,
  19. render,
  20. selectedProps,
  21. sizingWindowProps,
  22. }: Props<P>) {
  23. const defaultValues = Object.fromEntries(
  24. Object.entries(propMatrix).map(([key, values]) => {
  25. return [key, (values as any[]).at(0)];
  26. })
  27. );
  28. const values1 = propMatrix[selectedProps[0]] ?? [];
  29. const values2 = selectedProps.length === 2 ? propMatrix[selectedProps[1]] : undefined;
  30. const items = values1.flatMap(value1 => {
  31. const label = (
  32. <div>
  33. <JSXProperty name={String(selectedProps[0])} value={value1} />
  34. </div>
  35. );
  36. const content = (values2 ?? ['']).map(value2 => {
  37. return item(
  38. render,
  39. {
  40. ...defaultValues,
  41. [selectedProps[0]]: value1,
  42. ...(selectedProps.length === 2 ? {[selectedProps[1]]: value2} : {}),
  43. },
  44. sizingWindowProps
  45. );
  46. });
  47. return [label, ...content];
  48. });
  49. return (
  50. <div>
  51. {selectedProps.length === 2 ? (
  52. <h4 style={{margin: 0}}>
  53. <samp>{selectedProps[0] as string | number}</samp> vs{' '}
  54. <samp>{selectedProps[1] as string | number}</samp>
  55. </h4>
  56. ) : (
  57. <h4 style={{margin: 0}}>
  58. <samp>{selectedProps[0] as string | number}</samp>
  59. </h4>
  60. )}
  61. <Grid
  62. style={{
  63. gridTemplateColumns: `max-content repeat(${values2?.length ?? 1}, max-content)`,
  64. }}
  65. >
  66. {values2 ? <div key="space-head" /> : null}
  67. {values2?.map(value2 => (
  68. <div key={`title-2-${value2}`}>
  69. <JSXProperty name={String(selectedProps[1])} value={value2} />
  70. </div>
  71. ))}
  72. {items}
  73. </Grid>
  74. </div>
  75. );
  76. }
  77. // ((this: any, key: string, value: any) => any)
  78. function replacer(this: any, _key: string, value: any) {
  79. if (isValidElement(value)) {
  80. return 'react'; // value.name ?? value;
  81. }
  82. return value;
  83. }
  84. function item(Component, props, sizingWindowProps) {
  85. const hasChildren = 'children' in props;
  86. if (hasChildren) {
  87. const {children, ...otherProps} = props;
  88. return (
  89. <SizingWindow key={JSON.stringify(otherProps, replacer)} {...sizingWindowProps}>
  90. <Component {...otherProps}>{children}</Component>
  91. </SizingWindow>
  92. );
  93. }
  94. return (
  95. <SizingWindow key={JSON.stringify(props, replacer)} {...sizingWindowProps}>
  96. <Component {...props} />
  97. </SizingWindow>
  98. );
  99. }
  100. const Grid = styled('section')`
  101. display: grid;
  102. gap: ${space(1)};
  103. align-items: center;
  104. padding: var(--stories-grid-space);
  105. `;