-
Notifications
You must be signed in to change notification settings - Fork 4.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Mobile] Update the bottom sheet header #34309
Changes from 4 commits
2770d9d
8e07e65
8e7a218
1d2df4d
a716349
1f3d204
ee17e3d
1497dd0
c1ac20d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,91 @@ | ||||||
# Header | ||||||
enejb marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
|
||||||
Header components is meant to be used to compose the header inside the BottomSheet. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
It took me a bit to fully process how this component works, so think adding a bit more detail to the intro might help with clarity. WDYT? |
||||||
|
||||||
### Usage | ||||||
See | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
It looks like this can either be removed or perhaps there was a missing referencing? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||||||
```jsx | ||||||
/** | ||||||
* External dependencies | ||||||
*/ | ||||||
import { View, Text } from 'react-native'; | ||||||
import { useNavigation } from '@react-navigation/native'; | ||||||
|
||||||
/** | ||||||
* WordPress dependencies | ||||||
*/ | ||||||
import { BottomSheet } from '@wordpress/components'; | ||||||
|
||||||
const ExampleControl = () => { | ||||||
|
||||||
const goBack = () => {}; | ||||||
const applySettings = () => {}; | ||||||
|
||||||
return ( | ||||||
<BottomSheet.SubSheet> | ||||||
<> | ||||||
<BottomSheet.Header> | ||||||
<BottomSheet.Header.BackButton onPress={ goBack } /> | ||||||
<BottomSheet.Header.Title>{ 'Howdy' }</BottomSheet.Header.Title> | ||||||
<BottomSheet.Header.ApplyButton onPress={ applySetting } /> | ||||||
</BottomSheet.Header> | ||||||
<View paddingHorizontal={ 16 }> | ||||||
<Text>{ 'World' }</Text> | ||||||
</View> | ||||||
</> | ||||||
</BottomSheet.SubSheet> | ||||||
); | ||||||
}; | ||||||
|
||||||
export default ExampleControl; | ||||||
``` | ||||||
|
||||||
### Props | ||||||
Header component doesn't have any props. | ||||||
|
||||||
### Other Components | ||||||
Other components that Header component makes available. | ||||||
|
||||||
#### ApplyButton | ||||||
``` | ||||||
<Header.ApplyButton onPress={ goBack } /> | ||||||
``` | ||||||
|
||||||
The apply button is used to apply settings of the bottom sheet control. | ||||||
|
||||||
##### onPress | ||||||
use to pass a call back once the ApplyButton is clicked. | ||||||
enejb marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
|
||||||
#### BackButton | ||||||
``` | ||||||
<Header.BackButton onPress={ goBack } /> | ||||||
``` | ||||||
|
||||||
The back button is used to send the user back to the previous sheet. | ||||||
|
||||||
##### onPress | ||||||
use to pass a call back once the BackButton is clicked. | ||||||
enejb marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
|
||||||
#### CancelButton | ||||||
``` | ||||||
<Header.CancelButton onPress={ goBack } /> | ||||||
``` | ||||||
|
||||||
The cancel button is used to send the user back to the previous sheet. Closes the sheet. Use this if you are using a full screen bottom sheet. | ||||||
|
||||||
##### onPress | ||||||
use to pass a call back once the CancelButton is clicked. | ||||||
enejb marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
|
||||||
#### CloseButton | ||||||
``` | ||||||
<Header.CloseButton onPress={ goBack } /> | ||||||
``` | ||||||
|
||||||
The close button is used to closes the bottom sheet. Use this if you are using a full screen bottom sheet. | ||||||
|
||||||
#### Title | ||||||
``` | ||||||
<Header.Title>{ 'Howdy' }</Header.Title> | ||||||
``` | ||||||
|
||||||
The title is used to display the title of the bottom sheet control. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
import { View, TouchableWithoutFeedback } from 'react-native'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import styles from './styles.scss'; | ||
|
||
function ActionButton( { | ||
onPress, | ||
accessibilityLabel, | ||
accessibilityHint, | ||
children, | ||
} ) { | ||
return ( | ||
<TouchableWithoutFeedback | ||
dcalhoun marked this conversation as resolved.
Show resolved
Hide resolved
|
||
onPress={ onPress } | ||
accessibilityRole={ 'button' } | ||
accessibilityLabel={ accessibilityLabel } | ||
accessibilityHint={ accessibilityHint } | ||
> | ||
<View style={ styles[ 'action-button' ] }>{ children }</View> | ||
</TouchableWithoutFeedback> | ||
); | ||
} | ||
|
||
export default ActionButton; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
import { View, Text, Platform } from 'react-native'; | ||
|
||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { __ } from '@wordpress/i18n'; | ||
import { Icon, check } from '@wordpress/icons'; | ||
import { usePreferredColorSchemeStyle } from '@wordpress/compose'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import styles from './styles.scss'; | ||
import ActionButton from './action-button'; | ||
|
||
function ApplyButton( { onPress } ) { | ||
const buttonTextStyle = usePreferredColorSchemeStyle( | ||
styles[ 'button-text' ], | ||
styles[ 'button-text-dark' ] | ||
); | ||
|
||
const applyButtonStyle = usePreferredColorSchemeStyle( | ||
styles[ 'apply-button-icon' ], | ||
styles[ 'apply-button-icon-dark' ] | ||
); | ||
|
||
return ( | ||
<View style={ styles[ 'apply-button' ] }> | ||
<ActionButton | ||
onPress={ onPress } | ||
accessibilityLabel={ __( 'Apply' ) } | ||
accessibilityHint={ __( 'Applies the setting' ) } | ||
> | ||
{ Platform.OS === 'ios' ? ( | ||
<Text style={ buttonTextStyle } maxFontSizeMultiplier={ 2 }> | ||
{ __( 'Apply' ) } | ||
</Text> | ||
) : ( | ||
<Icon | ||
icon={ check } | ||
size={ 24 } | ||
style={ applyButtonStyle } | ||
/> | ||
) } | ||
</ActionButton> | ||
</View> | ||
); | ||
} | ||
|
||
export default ApplyButton; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
import { View, Platform, Text } from 'react-native'; | ||
|
||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { __ } from '@wordpress/i18n'; | ||
import { Icon, arrowLeft, close } from '@wordpress/icons'; | ||
import { usePreferredColorSchemeStyle } from '@wordpress/compose'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import styles from './styles.scss'; | ||
import ActionButton from './action-button'; | ||
import chevronBack from './../chevron-back'; | ||
|
||
function Button( { onPress, icon, text } ) { | ||
const buttonTextStyle = usePreferredColorSchemeStyle( | ||
styles[ 'button-text' ], | ||
styles[ 'button-text-dark' ] | ||
); | ||
|
||
return ( | ||
<View style={ styles[ 'back-button' ] }> | ||
<ActionButton | ||
onPress={ onPress } | ||
accessibilityLabel={ __( 'Go back' ) } | ||
accessibilityHint={ __( | ||
'Navigates to the previous content sheet' | ||
) } | ||
dcalhoun marked this conversation as resolved.
Show resolved
Hide resolved
|
||
> | ||
{ icon } | ||
{ text && ( | ||
<Text style={ buttonTextStyle } maxFontSizeMultiplier={ 2 }> | ||
{ text } | ||
</Text> | ||
) } | ||
</ActionButton> | ||
</View> | ||
); | ||
} | ||
|
||
function BackButton( { onPress } ) { | ||
const chevronLeftStyle = usePreferredColorSchemeStyle( | ||
styles[ 'chevron-left-icon' ], | ||
styles[ 'chevron-left-icon-dark' ] | ||
); | ||
const arrowLeftStyle = usePreferredColorSchemeStyle( | ||
styles[ 'arrow-left-icon' ], | ||
styles[ 'arrow-right-icon-dark' ] | ||
); | ||
|
||
let backIcon; | ||
let backText; | ||
|
||
if ( Platform.OS === 'ios' ) { | ||
backIcon = ( | ||
<Icon icon={ chevronBack } size={ 21 } style={ chevronLeftStyle } /> | ||
); | ||
backText = __( 'Back' ); | ||
} else { | ||
backIcon = ( | ||
<Icon icon={ arrowLeft } size={ 24 } style={ arrowLeftStyle } /> | ||
); | ||
} | ||
|
||
return <Button onPress={ onPress } icon={ backIcon } text={ backText } />; | ||
} | ||
|
||
function CancelButton( { onPress, text } ) { | ||
const arrowLeftStyle = usePreferredColorSchemeStyle( | ||
styles[ 'arrow-left-icon' ], | ||
styles[ 'arrow-right-icon-dark' ] | ||
); | ||
|
||
let backIcon; | ||
let backText; | ||
|
||
if ( Platform.OS === 'ios' ) { | ||
backText = text ? text : __( 'Cancel' ); | ||
} else { | ||
backIcon = <Icon icon={ close } size={ 24 } style={ arrowLeftStyle } />; | ||
} | ||
|
||
return <Button onPress={ onPress } icon={ backIcon } text={ backText } />; | ||
} | ||
|
||
function CloseButton( { onPress } ) { | ||
return <CancelButton onPress={ onPress } text={ __( 'Close' ) } />; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure if the small text change to "Close" merits an entire component. I'm not sure it provides value. WDYT about leveraging <CancelButton onPress={ () => {} } text={ __( 'Close' ) } /> There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am not a big fan of the
but in this case, because we are not just rendering all the children but only in special cases. (only on iOS) having a prop of Another way we would accomplish the same thing would be if a had a specific some sort of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Coming back to think I think once we have more specific accessibility Hint and specific content.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't have strong opinions about the granularity of the components in this case, but I wanted to mention that there may be cases where "Close" "Cancel" and "Back" are use somewhat inconsistently. The componentization in this PR might also be a good opportunity to clarify the intended semantics of these. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Understandable. I think relying upon That said, would it be better to communicate the conditional rendering via the property name? E.g. <BottomSheet.Header.CancelButton iosText={ __('Close') } />
I agree that moving to an abstraction where there is a "type" prop is likely too far an abstraction (also We could create "types" including one for the "Cancel" scenario, but it sort of puts us right back into the original limitation I hit. What if we encounter a new context where we need "Dismiss" text? If we encounter a new context, do we want to add another "type" or pass the required text via a freeform
The last two quotes are from each of @enejb and @mkevins separately, but I think you are both saying the same thing — that you might prefer to keep |
||
|
||
Button.Back = BackButton; | ||
Button.Cancel = CancelButton; | ||
Button.Close = CloseButton; | ||
|
||
export default Button; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
import { View } from 'react-native'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import styles from './styles.scss'; | ||
import Button from './back-button'; | ||
import ApplyButton from './apply-button'; | ||
import Title from './title'; | ||
|
||
function Header( { children } ) { | ||
return <View style={ styles.header }>{ children }</View>; | ||
} | ||
|
||
Header.ApplyButton = ApplyButton; | ||
Header.BackButton = Button.Back; | ||
Header.CancelButton = Button.Cancel; | ||
Header.CloseButton = Button.Close; | ||
Header.Title = Title; | ||
|
||
export default Header; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have largely seen "compound" components used to share state via React Context. I have not often seen "compound" components created largely for style application, i.e. providing the structural styles needed to keep the header UI consistent.
I think this use is really smart, and could work well for (1) ensuring the styles are applied consistently and (2) provide the ability to compose the elements as necessary for each context.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BottomSheet.Header.Title
I am not really sure if this is desired. I kind of like it mostly because it gives you a context where the compone is meant to be used but this same "advantage" makes it harder to reuse the same component.
For example.
but I guess at that point we could refactor it to look be part of the bottom sheet title component. But all that could be avoided if we made the component a bottom sheet component at this point. Thought?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a thought, but maybe "title" is not the best choice here 🤔 imho there should only be one title on a sheet.. so maybe label or heading is a better name?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with both of your perspectives. I suppose there is a fine line between naming the is too restrictive and naming so generic that it does not communicate intent.
I do like "heading" instead of "title." If we desire to allow these components to be used in a footer, we could replace "header" with "navbar."