@solo-io-public/ui-components 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +167 -0
- package/dist/Button.style-CCplEN2o.js +474 -0
- package/dist/Button.style-CCplEN2o.js.map +1 -0
- package/dist/Button.style-CI6Xbod7.cjs +10 -0
- package/dist/Button.style-CI6Xbod7.cjs.map +1 -0
- package/dist/_internal/Svg.d.ts +15 -0
- package/dist/_internal/colors.d.ts +17 -0
- package/dist/_internal/gradients.d.ts +11 -0
- package/dist/_internal/hexAddAlpha.d.ts +3 -0
- package/dist/_internal/palette.d.ts +30 -0
- package/dist/_internal/sizing.d.ts +14 -0
- package/dist/_internal/unstyledButton.d.ts +8 -0
- package/dist/_internal/utils.d.ts +7 -0
- package/dist/components/Alert/Alert.d.ts +17 -0
- package/dist/components/Alert/index.d.ts +1 -0
- package/dist/components/Button/Button.d.ts +32 -0
- package/dist/components/Button/Button.style.d.ts +48 -0
- package/dist/components/Button/index.d.ts +1 -0
- package/dist/components/CloseButton/CloseButton.d.ts +14 -0
- package/dist/components/CloseButton/index.d.ts +1 -0
- package/dist/components/Layout/FlexLayout.d.ts +83 -0
- package/dist/components/Layout/Spacer.d.ts +80 -0
- package/dist/components/Layout/index.d.ts +2 -0
- package/dist/components/MonacoEditor/EditorSettingsContext.d.ts +15 -0
- package/dist/components/MonacoEditor/MonacoEditorWithSettings.d.ts +20 -0
- package/dist/components/MonacoEditor/index.d.ts +2 -0
- package/dist/components/Text/Text.d.ts +71 -0
- package/dist/components/Text/index.d.ts +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/providers/SoloContextProvider.d.ts +62 -0
- package/dist/providers/SoloModeContext.d.ts +5 -0
- package/dist/providers/index.d.ts +2 -0
- package/dist/solo-components.cjs +56 -0
- package/dist/solo-components.cjs.map +1 -0
- package/dist/solo-components.js +664 -0
- package/dist/solo-components.js.map +1 -0
- package/dist/styles.cjs +2 -0
- package/dist/styles.cjs.map +1 -0
- package/dist/styles.d.ts +1 -0
- package/dist/styles.js +5 -0
- package/dist/styles.js.map +1 -0
- package/package.json +109 -0
- package/src/_internal/Svg.tsx +58 -0
- package/src/_internal/colors.ts +26 -0
- package/src/_internal/gradients.ts +36 -0
- package/src/_internal/hexAddAlpha.ts +9 -0
- package/src/_internal/palette.ts +46 -0
- package/src/_internal/sizing.ts +15 -0
- package/src/_internal/unstyledButton.ts +17 -0
- package/src/_internal/utils.ts +11 -0
- package/src/components/Alert/Alert.stories.tsx +44 -0
- package/src/components/Alert/Alert.tsx +168 -0
- package/src/components/Alert/index.ts +1 -0
- package/src/components/Button/Button.stories.tsx +62 -0
- package/src/components/Button/Button.style.ts +158 -0
- package/src/components/Button/Button.test.tsx +47 -0
- package/src/components/Button/Button.tsx +84 -0
- package/src/components/Button/index.ts +11 -0
- package/src/components/CloseButton/CloseButton.tsx +76 -0
- package/src/components/CloseButton/index.ts +1 -0
- package/src/components/Layout/FlexLayout.tsx +101 -0
- package/src/components/Layout/Spacer.tsx +131 -0
- package/src/components/Layout/index.ts +2 -0
- package/src/components/MonacoEditor/EditorSettingsContext.test.tsx +55 -0
- package/src/components/MonacoEditor/EditorSettingsContext.tsx +63 -0
- package/src/components/MonacoEditor/MonacoEditor.stories.tsx +197 -0
- package/src/components/MonacoEditor/MonacoEditorWithSettings.tsx +376 -0
- package/src/components/MonacoEditor/index.ts +9 -0
- package/src/components/Text/Text.stories.tsx +63 -0
- package/src/components/Text/Text.tsx +65 -0
- package/src/components/Text/index.ts +1 -0
- package/src/index.ts +7 -0
- package/src/providers/SoloContextProvider.tsx +213 -0
- package/src/providers/SoloModeContext.tsx +13 -0
- package/src/providers/index.ts +10 -0
- package/src/styles.ts +5 -0
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { css } from '@emotion/react';
|
|
2
|
+
import styled from '@emotion/styled';
|
|
3
|
+
import type { CSSProperties } from 'react';
|
|
4
|
+
import { cssProp, dontForwardProps } from '../../_internal/utils';
|
|
5
|
+
import { Spacer, type SpacerProps } from './Spacer';
|
|
6
|
+
|
|
7
|
+
// region Component
|
|
8
|
+
|
|
9
|
+
export type FlexLayoutProps = SpacerProps & {
|
|
10
|
+
horizontal?: boolean;
|
|
11
|
+
vertical?: boolean;
|
|
12
|
+
gap?: CSSProperties['gap'] | number;
|
|
13
|
+
display?: CSSProperties['display'];
|
|
14
|
+
flexWrap?: CSSProperties['flexWrap'];
|
|
15
|
+
justifyContent?: CSSProperties['justifyContent'];
|
|
16
|
+
alignItems?: CSSProperties['alignItems'];
|
|
17
|
+
alignContent?: CSSProperties['alignContent'];
|
|
18
|
+
flexDirection?: CSSProperties['flexDirection'];
|
|
19
|
+
overflow?: CSSProperties['overflow'];
|
|
20
|
+
column?: boolean;
|
|
21
|
+
row?: boolean;
|
|
22
|
+
nowrap?: boolean;
|
|
23
|
+
expandChildren?: boolean;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Quick horizontal, vertical, or centered (horizontal & vertical) flex layout.
|
|
28
|
+
* A numeric `gap` is treated as a 0.25em scale step (e.g. `gap={3}` → `0.75em`).
|
|
29
|
+
*/
|
|
30
|
+
export const FlexLayout = styled(
|
|
31
|
+
Spacer,
|
|
32
|
+
dontForwardProps(
|
|
33
|
+
'horizontal',
|
|
34
|
+
'vertical',
|
|
35
|
+
'gap',
|
|
36
|
+
'display',
|
|
37
|
+
'flexWrap',
|
|
38
|
+
'justifyContent',
|
|
39
|
+
'alignItems',
|
|
40
|
+
'alignContent',
|
|
41
|
+
'flexDirection',
|
|
42
|
+
'overflow',
|
|
43
|
+
'column',
|
|
44
|
+
'row',
|
|
45
|
+
'nowrap',
|
|
46
|
+
'expandChildren'
|
|
47
|
+
)
|
|
48
|
+
)<FlexLayoutProps>(({
|
|
49
|
+
horizontal,
|
|
50
|
+
vertical,
|
|
51
|
+
gap,
|
|
52
|
+
display,
|
|
53
|
+
flexWrap,
|
|
54
|
+
nowrap,
|
|
55
|
+
justifyContent,
|
|
56
|
+
alignItems,
|
|
57
|
+
alignContent,
|
|
58
|
+
flexDirection,
|
|
59
|
+
column,
|
|
60
|
+
row,
|
|
61
|
+
maxWidth,
|
|
62
|
+
minWidth,
|
|
63
|
+
maxHeight,
|
|
64
|
+
expandChildren,
|
|
65
|
+
overflow
|
|
66
|
+
}) => {
|
|
67
|
+
if (typeof gap === 'number') gap = gap * 0.25 + 'em';
|
|
68
|
+
return css`
|
|
69
|
+
display: flex;
|
|
70
|
+
${cssProp('justify-content', justifyContent ? justifyContent : horizontal ? 'center' : undefined)}
|
|
71
|
+
${cssProp('align-items', alignItems ? alignItems : vertical ? 'center' : undefined)}
|
|
72
|
+
${cssProp('align-content', alignContent ? alignContent : vertical ? 'center' : undefined)}
|
|
73
|
+
${cssProp('flex-direction', flexDirection ? flexDirection : column ? 'column' : row ? 'row' : undefined)}
|
|
74
|
+
${cssProp('gap', gap)}
|
|
75
|
+
${cssProp('display', display)}
|
|
76
|
+
${cssProp('flex-wrap', flexWrap ? flexWrap : nowrap ? 'nowrap' : 'wrap')}
|
|
77
|
+
${cssProp('overflow', overflow)}
|
|
78
|
+
max-width: ${maxWidth ?? '100%'};
|
|
79
|
+
${minWidth
|
|
80
|
+
? css`
|
|
81
|
+
min-width: ${minWidth};
|
|
82
|
+
`
|
|
83
|
+
: ''}
|
|
84
|
+
max-height: ${maxHeight ?? '100%'};
|
|
85
|
+
${expandChildren &&
|
|
86
|
+
css`
|
|
87
|
+
> * {
|
|
88
|
+
flex-grow: 1;
|
|
89
|
+
width: auto;
|
|
90
|
+
}
|
|
91
|
+
`}
|
|
92
|
+
`;
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
/** Adds a `flex-grow: 1` filler into a flex layout. */
|
|
96
|
+
export const FlexLayoutSpacer = styled.div<{ flexBasis?: CSSProperties['flexBasis'] }>(
|
|
97
|
+
({ flexBasis }) => css`
|
|
98
|
+
flex-grow: 1;
|
|
99
|
+
${cssProp('flex-basis', flexBasis)}
|
|
100
|
+
`
|
|
101
|
+
);
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import type { SerializedStyles } from '@emotion/react';
|
|
2
|
+
import styled from '@emotion/styled';
|
|
3
|
+
import type { ComponentProps, CSSProperties } from 'react';
|
|
4
|
+
import { cssProp, dontForwardProps } from '../../_internal/utils';
|
|
5
|
+
|
|
6
|
+
// region Helpers
|
|
7
|
+
|
|
8
|
+
type SizeType = number | string;
|
|
9
|
+
|
|
10
|
+
// region Component
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Margin/padding/sizing primitive. Use a string for exact values
|
|
14
|
+
* (e.g. `mx='5px'`). Directional props (`mr`, `mb`, …) override the axis
|
|
15
|
+
* shorthands (`mx`, `my`).
|
|
16
|
+
*/
|
|
17
|
+
export type SpacerProps = {
|
|
18
|
+
mx?: string;
|
|
19
|
+
my?: string;
|
|
20
|
+
mr?: string;
|
|
21
|
+
ml?: string;
|
|
22
|
+
mt?: string;
|
|
23
|
+
mb?: string;
|
|
24
|
+
margin?: string;
|
|
25
|
+
px?: string;
|
|
26
|
+
py?: string;
|
|
27
|
+
pr?: string;
|
|
28
|
+
pl?: string;
|
|
29
|
+
pt?: string;
|
|
30
|
+
pb?: string;
|
|
31
|
+
padding?: string;
|
|
32
|
+
display?: CSSProperties['display'];
|
|
33
|
+
position?: CSSProperties['position'];
|
|
34
|
+
minHeight?: SizeType;
|
|
35
|
+
minWidth?: CSSProperties['minWidth'];
|
|
36
|
+
height?: SizeType;
|
|
37
|
+
width?: SizeType;
|
|
38
|
+
maxHeight?: CSSProperties['maxHeight'];
|
|
39
|
+
maxWidth?: CSSProperties['maxWidth'];
|
|
40
|
+
flexGrow?: CSSProperties['flexGrow'];
|
|
41
|
+
flexBasis?: CSSProperties['flexBasis'];
|
|
42
|
+
flexShrink?: CSSProperties['flexShrink'];
|
|
43
|
+
overflowY?: CSSProperties['overflowY'];
|
|
44
|
+
overflowX?: CSSProperties['overflowX'];
|
|
45
|
+
textOverflow?: CSSProperties['textOverflow'];
|
|
46
|
+
overflow?: CSSProperties['overflow'];
|
|
47
|
+
zIndex?: CSSProperties['zIndex'];
|
|
48
|
+
stylingOverrides?: SerializedStyles;
|
|
49
|
+
fadeIn?: boolean;
|
|
50
|
+
} & ComponentProps<'div'>;
|
|
51
|
+
|
|
52
|
+
export const Spacer = styled(
|
|
53
|
+
'div',
|
|
54
|
+
dontForwardProps(
|
|
55
|
+
'mx',
|
|
56
|
+
'my',
|
|
57
|
+
'mr',
|
|
58
|
+
'ml',
|
|
59
|
+
'mt',
|
|
60
|
+
'mb',
|
|
61
|
+
'margin',
|
|
62
|
+
'px',
|
|
63
|
+
'py',
|
|
64
|
+
'pr',
|
|
65
|
+
'pl',
|
|
66
|
+
'pt',
|
|
67
|
+
'pb',
|
|
68
|
+
'padding',
|
|
69
|
+
'display',
|
|
70
|
+
'position',
|
|
71
|
+
'minHeight',
|
|
72
|
+
'minWidth',
|
|
73
|
+
'height',
|
|
74
|
+
'width',
|
|
75
|
+
'maxHeight',
|
|
76
|
+
'maxWidth',
|
|
77
|
+
'flexGrow',
|
|
78
|
+
'flexBasis',
|
|
79
|
+
'flexShrink',
|
|
80
|
+
'overflowY',
|
|
81
|
+
'overflowX',
|
|
82
|
+
'textOverflow',
|
|
83
|
+
'overflow',
|
|
84
|
+
'zIndex',
|
|
85
|
+
'stylingOverrides',
|
|
86
|
+
'fadeIn'
|
|
87
|
+
)
|
|
88
|
+
)<SpacerProps>(props => {
|
|
89
|
+
const { mx, my, mr, ml, mt, mb, margin, px, py, pl, pr, pt, pb, padding } = props;
|
|
90
|
+
const marginRight = mr ?? mx;
|
|
91
|
+
const marginLeft = ml ?? mx;
|
|
92
|
+
const marginTop = mt ?? my;
|
|
93
|
+
const marginBottom = mb ?? my;
|
|
94
|
+
const paddingRight = pr ?? px;
|
|
95
|
+
const paddingLeft = pl ?? px;
|
|
96
|
+
const paddingTop = pt ?? py;
|
|
97
|
+
const paddingBottom = pb ?? py;
|
|
98
|
+
return `
|
|
99
|
+
${cssProp('display', props.display)}
|
|
100
|
+
${cssProp('position', props.position)}
|
|
101
|
+
${cssProp('margin', margin)}
|
|
102
|
+
${cssProp('margin-right', marginRight)}
|
|
103
|
+
${cssProp('margin-left', marginLeft)}
|
|
104
|
+
${cssProp('margin-top', marginTop)}
|
|
105
|
+
${cssProp('margin-bottom', marginBottom)}
|
|
106
|
+
${cssProp('padding', padding)}
|
|
107
|
+
${cssProp('padding-right', paddingRight)}
|
|
108
|
+
${cssProp('padding-left', paddingLeft)}
|
|
109
|
+
${cssProp('padding-top', paddingTop)}
|
|
110
|
+
${cssProp('padding-bottom', paddingBottom)}
|
|
111
|
+
${cssProp('min-height', props.minHeight)}
|
|
112
|
+
${cssProp('min-width', props.minWidth)}
|
|
113
|
+
${cssProp('height', props.height)}
|
|
114
|
+
${cssProp('width', props.width)}
|
|
115
|
+
${cssProp('max-height', props.maxHeight)}
|
|
116
|
+
${cssProp('max-width', props.maxWidth)}
|
|
117
|
+
${cssProp('flex-grow', props.flexGrow)}
|
|
118
|
+
${cssProp('flex-shrink', props.flexShrink)}
|
|
119
|
+
${cssProp('flex-basis', props.flexBasis)}
|
|
120
|
+
${cssProp('overflow-y', props.overflowY)}
|
|
121
|
+
${cssProp('overflow-x', props.overflowX)}
|
|
122
|
+
${cssProp('overflow', props.overflow)}
|
|
123
|
+
${cssProp('text-overflow', props.textOverflow)}
|
|
124
|
+
${cssProp('z-index', props.zIndex)}
|
|
125
|
+
|
|
126
|
+
animation: ${props.fadeIn ? `fadeIn 200ms cubic-bezier(0.22, 1, 0.36, 1) forwards` : 'none'};
|
|
127
|
+
${props.fadeIn ? `@media (prefers-reduced-motion: reduce) { animation: none; opacity: 1; }` : ''}
|
|
128
|
+
|
|
129
|
+
${props.stylingOverrides?.styles ? props.stylingOverrides.styles : ''}
|
|
130
|
+
`;
|
|
131
|
+
});
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import { render, screen } from '@testing-library/react';
|
|
3
|
+
import userEvent from '@testing-library/user-event';
|
|
4
|
+
import { EditorSettingsProvider, useEditorSettings } from './EditorSettingsContext';
|
|
5
|
+
|
|
6
|
+
// Exercises the context only — Monaco itself can't render under happy-dom.
|
|
7
|
+
const Probe = () => {
|
|
8
|
+
const { vimEnabled, toggleVimMode, wordWrapEnabled, toggleWordWrap } = useEditorSettings();
|
|
9
|
+
return (
|
|
10
|
+
<div>
|
|
11
|
+
<span data-testid='vim'>{String(vimEnabled)}</span>
|
|
12
|
+
<span data-testid='wrap'>{String(wordWrapEnabled)}</span>
|
|
13
|
+
<button onClick={toggleVimMode}>toggle vim</button>
|
|
14
|
+
<button onClick={toggleWordWrap}>toggle wrap</button>
|
|
15
|
+
</div>
|
|
16
|
+
);
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const renderProbe = () =>
|
|
20
|
+
render(
|
|
21
|
+
<EditorSettingsProvider>
|
|
22
|
+
<Probe />
|
|
23
|
+
</EditorSettingsProvider>
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
describe('EditorSettingsContext', () => {
|
|
27
|
+
beforeEach(() => localStorage.clear());
|
|
28
|
+
|
|
29
|
+
it('defaults both settings to disabled', () => {
|
|
30
|
+
renderProbe();
|
|
31
|
+
expect(screen.getByTestId('vim')).toHaveTextContent('false');
|
|
32
|
+
expect(screen.getByTestId('wrap')).toHaveTextContent('false');
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('toggles vim mode and persists it to localStorage', async () => {
|
|
36
|
+
const user = userEvent.setup();
|
|
37
|
+
renderProbe();
|
|
38
|
+
await user.click(screen.getByRole('button', { name: 'toggle vim' }));
|
|
39
|
+
expect(screen.getByTestId('vim')).toHaveTextContent('true');
|
|
40
|
+
expect(localStorage.getItem('monaco-editor-vim-mode')).toBe('true');
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('hydrates initial state from localStorage', () => {
|
|
44
|
+
localStorage.setItem('monaco-editor-word-wrap', 'true');
|
|
45
|
+
renderProbe();
|
|
46
|
+
expect(screen.getByTestId('wrap')).toHaveTextContent('true');
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('throws when the hook is used outside the provider', () => {
|
|
50
|
+
// Suppress the expected React error log for the thrown render.
|
|
51
|
+
const spy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
52
|
+
expect(() => render(<Probe />)).toThrow(/within EditorSettingsProvider/);
|
|
53
|
+
spy.mockRestore();
|
|
54
|
+
});
|
|
55
|
+
});
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { createContext, useContext, useState, type ReactNode } from 'react';
|
|
2
|
+
|
|
3
|
+
// region Helpers
|
|
4
|
+
|
|
5
|
+
const VIM_MODE_STORAGE_KEY = 'monaco-editor-vim-mode';
|
|
6
|
+
const WORD_WRAP_STORAGE_KEY = 'monaco-editor-word-wrap';
|
|
7
|
+
|
|
8
|
+
export interface EditorSettings {
|
|
9
|
+
vimEnabled: boolean;
|
|
10
|
+
toggleVimMode: () => void;
|
|
11
|
+
wordWrapEnabled: boolean;
|
|
12
|
+
toggleWordWrap: () => void;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const EditorSettingsContext = createContext<EditorSettings | undefined>(undefined);
|
|
16
|
+
|
|
17
|
+
// region Component
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Provides per-user editor preferences (vim mode, word wrap), persisted to
|
|
21
|
+
* `localStorage`. Wrap any tree that renders `<MonacoEditorWithSettings />`.
|
|
22
|
+
*/
|
|
23
|
+
export function EditorSettingsProvider({ children }: { children: ReactNode }) {
|
|
24
|
+
const [vimEnabled, setVimEnabled] = useState(() => {
|
|
25
|
+
const saved = localStorage.getItem(VIM_MODE_STORAGE_KEY);
|
|
26
|
+
return saved === 'true';
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const [wordWrapEnabled, setWordWrapEnabled] = useState(() => {
|
|
30
|
+
const saved = localStorage.getItem(WORD_WRAP_STORAGE_KEY);
|
|
31
|
+
return saved === 'true';
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const toggleVimMode = () => {
|
|
35
|
+
setVimEnabled(prev => {
|
|
36
|
+
const newValue = !prev;
|
|
37
|
+
localStorage.setItem(VIM_MODE_STORAGE_KEY, String(newValue));
|
|
38
|
+
return newValue;
|
|
39
|
+
});
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const toggleWordWrap = () => {
|
|
43
|
+
setWordWrapEnabled(prev => {
|
|
44
|
+
const newValue = !prev;
|
|
45
|
+
localStorage.setItem(WORD_WRAP_STORAGE_KEY, String(newValue));
|
|
46
|
+
return newValue;
|
|
47
|
+
});
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<EditorSettingsContext.Provider value={{ vimEnabled, toggleVimMode, wordWrapEnabled, toggleWordWrap }}>
|
|
52
|
+
{children}
|
|
53
|
+
</EditorSettingsContext.Provider>
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function useEditorSettings() {
|
|
58
|
+
const context = useContext(EditorSettingsContext);
|
|
59
|
+
if (context === undefined) {
|
|
60
|
+
throw new Error('useEditorSettings must be used within EditorSettingsProvider');
|
|
61
|
+
}
|
|
62
|
+
return context;
|
|
63
|
+
}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { css } from "@emotion/react";
|
|
2
|
+
import type { Decorator, Meta, StoryObj } from "@storybook/react";
|
|
3
|
+
import { Drawer } from "antd";
|
|
4
|
+
import { useState } from "react";
|
|
5
|
+
import { Button } from "../Button";
|
|
6
|
+
import { CloseButton } from "../CloseButton";
|
|
7
|
+
import { MonacoEditorWithSettings } from "./MonacoEditorWithSettings";
|
|
8
|
+
|
|
9
|
+
const meta: Meta<typeof MonacoEditorWithSettings> = {
|
|
10
|
+
title: "Common / MonacoEditor",
|
|
11
|
+
component: MonacoEditorWithSettings,
|
|
12
|
+
args: {
|
|
13
|
+
language: "json",
|
|
14
|
+
height: "100%",
|
|
15
|
+
},
|
|
16
|
+
argTypes: {
|
|
17
|
+
language: {
|
|
18
|
+
control: "select",
|
|
19
|
+
options: ["json", "yaml", "javascript", "typescript", "html", "css"],
|
|
20
|
+
},
|
|
21
|
+
readOnly: { control: "boolean" },
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
// Sizes the inline editor stories; the Drawer story renders just its button.
|
|
26
|
+
const editorBox: Decorator = (Story) => (
|
|
27
|
+
<div style={{ height: 360 }}>
|
|
28
|
+
<Story />
|
|
29
|
+
</div>
|
|
30
|
+
);
|
|
31
|
+
export default meta;
|
|
32
|
+
|
|
33
|
+
type Story = StoryObj<typeof MonacoEditorWithSettings>;
|
|
34
|
+
|
|
35
|
+
export const Json: Story = {
|
|
36
|
+
name: "JSON",
|
|
37
|
+
decorators: [editorBox],
|
|
38
|
+
args: {
|
|
39
|
+
language: "json",
|
|
40
|
+
value: `{
|
|
41
|
+
"name": "@solo-io-public/ui-components",
|
|
42
|
+
"private": false,
|
|
43
|
+
"keep": "the button"
|
|
44
|
+
}`,
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export const Yaml: Story = {
|
|
49
|
+
name: "YAML",
|
|
50
|
+
decorators: [editorBox],
|
|
51
|
+
args: {
|
|
52
|
+
language: "yaml",
|
|
53
|
+
value: `name: example
|
|
54
|
+
items:
|
|
55
|
+
- one
|
|
56
|
+
- two
|
|
57
|
+
nested:
|
|
58
|
+
enabled: true`,
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export const ReadOnly: Story = {
|
|
63
|
+
decorators: [editorBox],
|
|
64
|
+
args: {
|
|
65
|
+
language: "typescript",
|
|
66
|
+
readOnly: true,
|
|
67
|
+
value: `export const add = (a: number, b: number): number => a + b;`,
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const K8S_YAML = `apiVersion: apps/v1
|
|
72
|
+
kind: Deployment
|
|
73
|
+
metadata:
|
|
74
|
+
name: payments-api
|
|
75
|
+
namespace: payments
|
|
76
|
+
labels:
|
|
77
|
+
app.kubernetes.io/name: payments-api
|
|
78
|
+
app.kubernetes.io/instance: payments-api
|
|
79
|
+
app.kubernetes.io/version: "1.8.3"
|
|
80
|
+
app.kubernetes.io/component: api
|
|
81
|
+
app.kubernetes.io/part-of: payments-platform
|
|
82
|
+
app.kubernetes.io/managed-by: helm
|
|
83
|
+
annotations:
|
|
84
|
+
kubernetes.io/change-cause: "Deploy v1.8.3"
|
|
85
|
+
prometheus.io/scrape: "true"
|
|
86
|
+
prometheus.io/port: "9090"
|
|
87
|
+
spec:
|
|
88
|
+
replicas: 3
|
|
89
|
+
revisionHistoryLimit: 5
|
|
90
|
+
strategy:
|
|
91
|
+
type: RollingUpdate
|
|
92
|
+
rollingUpdate:
|
|
93
|
+
maxSurge: 1
|
|
94
|
+
maxUnavailable: 0
|
|
95
|
+
selector:
|
|
96
|
+
matchLabels:
|
|
97
|
+
app.kubernetes.io/name: payments-api
|
|
98
|
+
template:
|
|
99
|
+
metadata:
|
|
100
|
+
labels:
|
|
101
|
+
app.kubernetes.io/name: payments-api
|
|
102
|
+
app.kubernetes.io/instance: payments-api
|
|
103
|
+
spec:
|
|
104
|
+
serviceAccountName: payments-api
|
|
105
|
+
securityContext:
|
|
106
|
+
runAsNonRoot: true
|
|
107
|
+
runAsUser: 10001
|
|
108
|
+
fsGroup: 10001
|
|
109
|
+
containers:
|
|
110
|
+
- name: api
|
|
111
|
+
image: registry.example.com/payments/payments-api:1.8.3
|
|
112
|
+
imagePullPolicy: IfNotPresent
|
|
113
|
+
ports:
|
|
114
|
+
- name: http
|
|
115
|
+
containerPort: 8080
|
|
116
|
+
- name: metrics
|
|
117
|
+
containerPort: 9090
|
|
118
|
+
env:
|
|
119
|
+
- name: LOG_LEVEL
|
|
120
|
+
value: info
|
|
121
|
+
- name: DATABASE_URL
|
|
122
|
+
valueFrom:
|
|
123
|
+
secretKeyRef:
|
|
124
|
+
name: payments-db
|
|
125
|
+
key: url
|
|
126
|
+
- name: REDIS_HOST
|
|
127
|
+
value: redis.payments.svc.cluster.local
|
|
128
|
+
resources:
|
|
129
|
+
requests:
|
|
130
|
+
cpu: 250m
|
|
131
|
+
memory: 256Mi
|
|
132
|
+
limits:
|
|
133
|
+
cpu: "1"
|
|
134
|
+
memory: 512Mi
|
|
135
|
+
readinessProbe:
|
|
136
|
+
httpGet:
|
|
137
|
+
path: /healthz/ready
|
|
138
|
+
port: http
|
|
139
|
+
initialDelaySeconds: 5
|
|
140
|
+
periodSeconds: 10
|
|
141
|
+
livenessProbe:
|
|
142
|
+
httpGet:
|
|
143
|
+
path: /healthz/live
|
|
144
|
+
port: http
|
|
145
|
+
initialDelaySeconds: 15
|
|
146
|
+
periodSeconds: 20
|
|
147
|
+
volumeMounts:
|
|
148
|
+
- name: config
|
|
149
|
+
mountPath: /etc/payments
|
|
150
|
+
readOnly: true
|
|
151
|
+
volumes:
|
|
152
|
+
- name: config
|
|
153
|
+
configMap:
|
|
154
|
+
name: payments-api-config
|
|
155
|
+
`;
|
|
156
|
+
|
|
157
|
+
// Demonstrates the common "view a resource" pattern: a button opens a Drawer
|
|
158
|
+
// with the editor in read-only mode. The manifest is long enough to scroll,
|
|
159
|
+
// which also shows the controls shifting clear of the vertical scrollbar.
|
|
160
|
+
const ViewYamlDrawer = () => {
|
|
161
|
+
const [open, setOpen] = useState(false);
|
|
162
|
+
return (
|
|
163
|
+
<>
|
|
164
|
+
<Button color="dark-purple" onClick={() => setOpen(true)}>
|
|
165
|
+
View YAML
|
|
166
|
+
</Button>
|
|
167
|
+
<Drawer
|
|
168
|
+
title="payments-api.deployment.yaml"
|
|
169
|
+
placement="right"
|
|
170
|
+
width={680}
|
|
171
|
+
open={open}
|
|
172
|
+
onClose={() => setOpen(false)}
|
|
173
|
+
closable={false}
|
|
174
|
+
extra={<CloseButton aria-label="Close" onClick={() => setOpen(false)} />}
|
|
175
|
+
styles={{ body: { padding: 0 } }}
|
|
176
|
+
>
|
|
177
|
+
<MonacoEditorWithSettings
|
|
178
|
+
value={K8S_YAML}
|
|
179
|
+
language="yaml"
|
|
180
|
+
readOnly
|
|
181
|
+
height="100%"
|
|
182
|
+
// Full-bleed in the drawer: drop the rounded corners + side/top border
|
|
183
|
+
// so it sits flush under the drawer title (the drawer supplies the frame).
|
|
184
|
+
styleOverrides={css`
|
|
185
|
+
border-radius: 0;
|
|
186
|
+
border-width: 0 0 1px 0;
|
|
187
|
+
`}
|
|
188
|
+
/>
|
|
189
|
+
</Drawer>
|
|
190
|
+
</>
|
|
191
|
+
);
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
export const ViewInDrawer: Story = {
|
|
195
|
+
name: "View YAML in Drawer",
|
|
196
|
+
render: () => <ViewYamlDrawer />,
|
|
197
|
+
};
|