@@ -2,85 +2,125 @@ import CogIcon from '@patternfly/react-icons/dist/esm/icons/cog-icon';
22import MapIcon from '@patternfly/react-icons/dist/esm/icons/map-icon' ;
33import MoonIcon from '@patternfly/react-icons/dist/esm/icons/moon-icon' ;
44import HashtagIcon from '@patternfly/react-icons/dist/esm/icons/hashtag-icon' ;
5+ import FontIcon from '@patternfly/react-icons/dist/esm/icons/font-icon' ;
56import { CodeEditor , CodeEditorControl } from '@patternfly/react-code-editor' ;
6- import { Flex , FlexItem , Icon , Modal , ModalBody , ModalHeader , Switch , SwitchProps } from '@patternfly/react-core' ;
7+ import {
8+ Flex ,
9+ FlexItem ,
10+ Icon ,
11+ Modal ,
12+ ModalBody ,
13+ ModalHeader ,
14+ NumberInput ,
15+ Switch ,
16+ SwitchProps
17+ } from '@patternfly/react-core' ;
718import { useState } from 'react' ;
819
920interface ConfigModalItemProps {
1021 /** Icon rendered inside the configuration modal. */
1122 icon ?: React . ReactNode ;
1223 /** Description of the configuration option. */
1324 description : string ;
14- /** Flag indicating whether the option is enabled or disabled. */
15- isChecked ?: SwitchProps [ 'isChecked' ] ;
16- /** onChange handler for the switch. */
17- onChange ?: SwitchProps [ 'onChange' ] ;
1825 /** Title of the configuration option. We assume that titles are unique. */
1926 title : string ;
20- /** Labels for the enabled and disabled states of the switch. */
21- labels ?: {
22- enabled : string ;
23- disabled : string ;
24- } ;
25- /** Optional OUIA ID of the configuration option. Also used as a prefix for the ids of inner elements. */
26- ouiaId ?: string ;
27+ /**
28+ * Optional ID of the configuration option. Also used as a prefix for the ids of inner elements and the OUIA id.
29+ * - `${ouiaId}-title` for the element which contains the title
30+ * - `${ouiaId}-description` for the element which contains the description
31+ */
32+ id ?: string ;
33+ /**
34+ * Slot to render inside the configuration modal. Remember to add `aria-labelledby` and `aria-describedby` props
35+ * to the control inside the slot, pointing to the title and description ids respectively.
36+ */
37+ slot ?: React . ReactNode ;
2738}
2839
2940const ConfigModalItem : React . FunctionComponent < ConfigModalItemProps > = ( {
3041 icon = < CogIcon /> ,
3142 description,
32- isChecked = false ,
33- labels = { enabled : undefined , disabled : undefined } ,
34- onChange,
3543 title,
36- ouiaId
44+ id = `ConfigModalItem-${ title . replace ( / \s + / g, '-' ) . toLowerCase ( ) } ` ,
45+ slot
3746} ) => (
3847 < Flex
3948 alignItems = { { default : 'alignItemsCenter' } }
4049 justifyContent = { { default : 'justifyContentSpaceBetween' } }
4150 spaceItems = { { default : 'spaceItemsMd' } }
51+ id = { id }
4252 >
4353 < FlexItem flex = { { default : 'flex_1' } } >
4454 < Flex spaceItems = { { default : 'spaceItemsSm' } } >
4555 < Icon isInline > { icon } </ Icon >
46- < strong id = { `${ ouiaId } -title` } className = "pf-v6-u-font-weight-bold" >
56+ < strong id = { `${ id } -title` } className = "pf-v6-u-font-weight-bold" >
4757 { title }
4858 </ strong >
4959 </ Flex >
5060
51- < div id = { `${ ouiaId } -description` } > { description } </ div >
61+ < div id = { `${ id } -description` } > { description } </ div >
5262 </ FlexItem >
53- < FlexItem alignSelf = { { default : 'alignSelfCenter' } } >
63+ < FlexItem alignSelf = { { default : 'alignSelfCenter' } } > { slot } </ FlexItem >
64+ </ Flex >
65+ ) ;
66+
67+ interface ConfigModalSwitchProps extends Omit < ConfigModalItemProps , 'slot' > {
68+ /** Flag indicating whether the option is enabled or disabled. */
69+ isChecked ?: SwitchProps [ 'isChecked' ] ;
70+ /** onChange handler for the switch. */
71+ onChange ?: SwitchProps [ 'onChange' ] ;
72+ /** Labels for the enabled and disabled states of the switch. */
73+ labels ?: {
74+ enabled : string ;
75+ disabled : string ;
76+ } ;
77+ }
78+
79+ const ConfigModalSwitch : React . FunctionComponent < ConfigModalSwitchProps > = ( {
80+ icon = < CogIcon /> ,
81+ description,
82+ title,
83+ id = `ConfigModalSwitch-${ title . replace ( / \s + / g, '-' ) . toLowerCase ( ) } ` ,
84+ isChecked = false ,
85+ onChange,
86+ labels = { enabled : undefined , disabled : undefined }
87+ } ) => (
88+ < ConfigModalItem
89+ icon = { icon }
90+ description = { description }
91+ title = { title }
92+ id = { id }
93+ slot = {
5494 < Switch
55- aria-labelledby = { `${ ouiaId } -title` }
56- aria-describedby = { `${ ouiaId } -description` }
57- ouiaId = { `${ ouiaId } -switch` }
95+ aria-labelledby = { `${ id } -title` }
96+ aria-describedby = { `${ id } -description` }
97+ ouiaId = { `${ id } -switch` }
5898 isChecked = { isChecked }
5999 isReversed
60100 label = { isChecked ? labels . enabled : labels . disabled }
61101 onChange = { onChange }
62102 />
63- </ FlexItem >
64- </ Flex >
103+ }
104+ / >
65105) ;
66106
67107interface ConfigModalControlProps {
68- /** Array of configuration controls to be rendered inside the modal. */
69- controls : ConfigModalItemProps [ ] ;
108+ /** Controls to be rendered inside the configuration modal. */
109+ children : React . ReactNode ;
70110 /** Title of the configuration modal. */
71111 title ?: string ;
72112 /** Description of the configuration modal. */
73113 description ?: string ;
74- /** Optional OUIA ID of the configuration modal. Also used as a prefix for the ids of inner elements. */
114+ /** OptionalID of the configuration modal. Also used as a prefix for the ids of inner elements and the OUIA id . */
75115 ouiaId ?: string ;
76116}
77117
78118const ConfigModalControl : React . FunctionComponent < ConfigModalControlProps > = ( {
79- controls ,
119+ children ,
80120 title = 'Editor settings' ,
81121 description = 'Settings will be applied immediately' ,
82122 ouiaId = 'CodeEditorConfigurationModal'
83- } ) => {
123+ } : ConfigModalControlProps ) => {
84124 const [ isModalOpen , setIsModalOpen ] = useState ( false ) ;
85125
86126 return (
@@ -96,20 +136,14 @@ const ConfigModalControl: React.FunctionComponent<ConfigModalControlProps> = ({
96136 < ModalHeader title = { title } description = { description } labelId = { `${ ouiaId } -title` } />
97137 < ModalBody id = { `${ ouiaId } -body` } >
98138 < Flex direction = { { default : 'column' } } spaceItems = { { default : 'spaceItemsMd' } } >
99- { controls . map ( ( control ) => (
100- < ConfigModalItem
101- key = { control . title }
102- ouiaId = { `${ ouiaId } -${ control . title . replace ( / \s + / g, '-' ) . toLowerCase ( ) } ` }
103- { ...control }
104- />
105- ) ) }
139+ { children }
106140 </ Flex >
107141 </ ModalBody >
108142 </ Modal >
109143 < CodeEditorControl
110144 aria-label = { title }
111145 aria-haspopup = "dialog"
112- icon = { < CogIcon /> }
146+ isSettings
113147 onClick = { ( ) => setIsModalOpen ( true ) }
114148 tooltipProps = { { content : title , ariaLive : 'off' } }
115149 />
@@ -123,37 +157,61 @@ export const CodeEditorConfigurationModal: React.FunctionComponent = () => {
123157 const [ isMinimapVisible , setIsMinimapVisible ] = useState ( true ) ;
124158 const [ isDarkTheme , setIsDarkTheme ] = useState ( false ) ;
125159 const [ isLineNumbersVisible , setIsLineNumbersVisible ] = useState ( true ) ;
160+ const [ fontSize , setFontSize ] = useState ( 14 ) ;
126161
127162 const onChange = ( code : string ) => {
128163 setCode ( code ) ;
129164 } ;
130165
131166 const customControl = (
132- < ConfigModalControl
133- controls = { [
134- {
135- title : 'Minimap' ,
136- description : 'Show a preview of the full code on the side of the editor' ,
137- isChecked : isMinimapVisible ,
138- onChange : ( _e , checked ) => setIsMinimapVisible ( checked ) ,
139- icon : < MapIcon />
140- } ,
141- {
142- title : 'Dark theme' ,
143- description : 'Switch the editor to a dark color theme' ,
144- isChecked : isDarkTheme ,
145- onChange : ( _e , checked ) => setIsDarkTheme ( checked ) ,
146- icon : < MoonIcon />
147- } ,
148- {
149- title : 'Line numbers' ,
150- description : 'Show line numbers to the left of each line of code' ,
151- isChecked : isLineNumbersVisible ,
152- onChange : ( _e , checked ) => setIsLineNumbersVisible ( checked ) ,
153- icon : < HashtagIcon />
167+ < ConfigModalControl >
168+ < ConfigModalSwitch
169+ key = "minimap-switch"
170+ title = "Minimap"
171+ description = "Show a preview of the full code on the side of the editor"
172+ isChecked = { isMinimapVisible }
173+ onChange = { ( _e , checked ) => setIsMinimapVisible ( checked ) }
174+ icon = { < MapIcon /> }
175+ />
176+ < ConfigModalSwitch
177+ key = "dark-theme-switch"
178+ title = "Dark theme"
179+ description = "Switch the editor to a dark color theme"
180+ isChecked = { isDarkTheme }
181+ onChange = { ( _e , checked ) => setIsDarkTheme ( checked ) }
182+ icon = { < MoonIcon /> }
183+ />
184+ < ConfigModalSwitch
185+ key = "line-numbers-switch"
186+ title = "Line numbers"
187+ description = "Show line numbers to the left of each line of code"
188+ isChecked = { isLineNumbersVisible }
189+ onChange = { ( _e , checked ) => setIsLineNumbersVisible ( checked ) }
190+ icon = { < HashtagIcon /> }
191+ />
192+ < ConfigModalItem
193+ key = "font-size"
194+ title = "Font size"
195+ description = "Adjust the font size of the code editor"
196+ ouia-id = "ConfigModalItem-font-size"
197+ icon = { < FontIcon /> }
198+ slot = {
199+ < NumberInput
200+ aria-labelledby = "ConfigModalItem-font-size-title"
201+ aria-describedby = "ConfigModalItem-font-size-description"
202+ minusBtnAriaLabel = "Decrease font size"
203+ plusBtnAriaLabel = "Increase font size"
204+ role = "group" // For screen readers to group the input and buttons as one widget
205+ value = { fontSize }
206+ min = { 5 }
207+ onMinus = { ( ) => setFontSize ( ( size ) => Math . max ( 5 , size - 1 ) ) }
208+ onChange = { ( event ) => setFontSize ( Number ( ( event . target as HTMLInputElement ) . value ) ) }
209+ onPlus = { ( ) => setFontSize ( ( size ) => size + 1 ) }
210+ widthChars = { 2 }
211+ />
154212 }
155- ] }
156- / >
213+ />
214+ </ ConfigModalControl >
157215 ) ;
158216
159217 return (
@@ -165,6 +223,7 @@ export const CodeEditorConfigurationModal: React.FunctionComponent = () => {
165223 isLineNumbersVisible = { isLineNumbersVisible }
166224 isMinimapVisible = { isMinimapVisible }
167225 onChange = { onChange }
226+ options = { { fontSize } }
168227 />
169228 ) ;
170229} ;
0 commit comments