Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/pluggableWidgets/switch-native/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "switch-native",
"widgetName": "Switch",
"version": "1.1.0",
"version": "1.1.1",
"license": "Apache-2.0",
"repository": {
"type": "git",
Expand Down
93 changes: 65 additions & 28 deletions packages/pluggableWidgets/switch-native/src/Switch.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { flattenStyles } from "@mendix/piw-native-utils-internal";
import { ReactElement, useCallback } from "react";
import React, { ReactElement, useCallback } from "react";
import { View, Text, Switch as SwitchComponent, Platform } from "react-native";
import { executeAction } from "@mendix/piw-utils-internal";
import { extractStyles } from "@mendix/pluggable-widgets-tools";
Expand All @@ -10,7 +10,7 @@ import { SwitchStyle, defaultSwitchStyle, CheckBoxInputType } from "./ui/Styles"
export type Props = SwitchProps<SwitchStyle>;

export function Switch(props: Props): ReactElement {
const { label, labelOrientation, showLabel, name, onChange, booleanAttribute } = props;
const { label, labelOrientation, showLabel, name, onChange, booleanAttribute, labelPosition } = props;
const combinedStyles = flattenStyles(defaultSwitchStyle, props.style);
const styles = processStyles(combinedStyles);
const horizontalOrientation = showLabel && labelOrientation === "horizontal";
Expand Down Expand Up @@ -39,36 +39,73 @@ export function Switch(props: Props): ReactElement {

const labelValue = label?.status === "available" ? label.value : "";

const switchElement = (
<SwitchComponent
disabled={!editable}
testID={name}
style={inputStyle}
onValueChange={editable ? onChangeCallback : undefined}
value={booleanAttribute.value}
trackColor={{
true: inputProps.trackColorOn,
false: inputProps.trackColorOff
}}
thumbColor={booleanAttribute.value ? inputProps.thumbColorOn : inputProps.thumbColorOff}
{...(Platform.OS === "ios" ? { ios_backgroundColor: inputProps.trackColorOff } : {})}
/>
);

const labelElement = showLabel ? (
<Text testID={`${name}$label`} style={[labelStyles]}>
{labelValue}
</Text>
) : null;

const validationMessage = hasValidationMessage ? (
<Text testID={`${name}$alert`} style={styles.validationMessage}>
{booleanAttribute.validation}
</Text>
) : null;

return (
<View
testID={`${name}$wrapper`}
style={[containerStyles, horizontalOrientation ? { flexDirection: "row", alignItems: "center" } : null]}
style={[containerStyles, { flexDirection: "column", alignItems: "flex-start" }]}
>
{showLabel ? (
<Text testID={`${name}$label`} style={[labelStyles, horizontalOrientation ? { flex: 1 } : null]}>
{labelValue}
</Text>
) : null}
<View style={[horizontalOrientation ? { flex: 1, alignItems: "flex-end" } : { alignItems: "flex-start" }]}>
<SwitchComponent
disabled={!editable}
testID={name}
style={inputStyle}
onValueChange={editable ? onChangeCallback : undefined}
value={booleanAttribute.value}
trackColor={{
true: inputProps.trackColorOn,
false: inputProps.trackColorOff
}}
thumbColor={booleanAttribute.value ? inputProps.thumbColorOn : inputProps.thumbColorOff}
{...(Platform.OS === "ios" ? { ios_backgroundColor: inputProps.trackColorOff } : {})}
/>
{hasValidationMessage ? (
<Text testID={`${name}$alert`} style={styles.validationMessage}>
{booleanAttribute.validation}
</Text>
) : null}
</View>
{horizontalOrientation ? (
// Horizontal layout: label and switch in a row, validation message below
<>
<View
testID={`${name}$horizontalContainer`}
style={{
flexDirection: "row",
alignItems: "center",
width: "100%",
justifyContent: "space-between"
}}
>
{labelPosition === "right" ? (
<>
{React.cloneElement(switchElement, { key: "switch" })}
{labelElement && React.cloneElement(labelElement, { key: "label" })}
</>
) : (
<>
{labelElement && React.cloneElement(labelElement, { key: "label" })}
{React.cloneElement(switchElement, { key: "switch" })}
</>
)}
</View>
{validationMessage}
</>
) : (
// Vertical layout: label, switch, and validation message all in a column
<>
{labelElement && React.cloneElement(labelElement, { key: "label" })}
{React.cloneElement(switchElement, { key: "switch" })}
{validationMessage}
</>
)}
</View>
);
}
Expand Down
32 changes: 19 additions & 13 deletions packages/pluggableWidgets/switch-native/src/Switch.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<widget id="com.mendix.widget.native.switch.Switch" supportedPlatform="Native" needsEntityContext="true" offlineCapable="true"
pluginWidget="true" xmlns="http://www.mendix.com/widget/1.0/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.mendix.com/widget/1.0/ ../../../node_modules/mendix/custom_widget.xsd">
<?xml version="1.0" encoding="utf-8" ?>
<widget id="com.mendix.widget.native.switch.Switch" supportedPlatform="Native" needsEntityContext="true" offlineCapable="true" pluginWidget="true" xmlns="http://www.mendix.com/widget/1.0/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.mendix.com/widget/1.0/ ../../../node_modules/mendix/custom_widget.xsd">
<name>Switch</name>
<description>Toggle a boolean attribute.</description>
<studioProCategory>Display</studioProCategory>
Expand All @@ -11,44 +9,52 @@
<propertyGroup caption="Data source">
<property key="booleanAttribute" type="attribute">
<caption>Attribute</caption>
<description/>
<description />
<attributeTypes>
<attributeType name="Boolean"/>
<attributeType name="Boolean" />
</attributeTypes>
</property>
</propertyGroup>
<propertyGroup caption="Events">
<property key="onChange" type="action" required="false">
<caption>On change</caption>
<description/>
<description />
</property>
</propertyGroup>
<propertyGroup caption="Label">
<property key="showLabel" type="boolean" defaultValue="false">
<caption>Show label</caption>
<description/>
<description />
</property>
<property key="label" type="textTemplate" required="false">
<caption>Label</caption>
<description/>
<description />
</property>
<property key="labelOrientation" type="enumeration" defaultValue="horizontal">
<caption>Label orientation</caption>
<description/>
<description />
<enumerationValues>
<enumerationValue key="horizontal">Horizontal</enumerationValue>
<enumerationValue key="vertical">Vertical</enumerationValue>
</enumerationValues>
</property>
<property key="labelPosition" type="enumeration" defaultValue="left">
<caption>Label position</caption>
<description>The position of the label relative to the switch</description>
<enumerationValues>
<enumerationValue key="left">Left</enumerationValue>
<enumerationValue key="right">Right</enumerationValue>
</enumerationValues>
</property>
</propertyGroup>
<propertyGroup caption="Editability">
<systemProperty key="Editability"/>
<systemProperty key="Editability" />
</propertyGroup>
<propertyGroup caption="Visibility">
<systemProperty key="Visibility"/>
<systemProperty key="Visibility" />
</propertyGroup>
<propertyGroup caption="Common">
<systemProperty key="Name"/>
<systemProperty key="Name" />
</propertyGroup>
</propertyGroup>
</properties>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
showLabel: false,
booleanAttribute: new EditableValueBuilder<boolean>().withValue(false).build(),
onChange: undefined,
style: [{ ...defaultSwitchStyle, ...style }]
style: [{ ...defaultSwitchStyle, ...style }],
labelPosition: "left"
};

return { ...defaultProps, ...props };
Expand All @@ -23,7 +24,7 @@
let Platform: any;

beforeEach(() => {
Platform = require("react-native").Platform;

Check warning on line 27 in packages/pluggableWidgets/switch-native/src/__tests__/Switch.spec.tsx

View workflow job for this annotation

GitHub Actions / Unit tests

Require statement not part of import statement
});

it("with editable value renders enabled", () => {
Expand Down Expand Up @@ -55,14 +56,23 @@
expect(screen.getByTestId(`${name}$label`)).toBeTruthy();
});

it("with showLabel true renders label horizontally", () => {
it("with showLabel true and horizontal orientation, renders label and switch in a row", () => {
const props = createProps({
showLabel: true
showLabel: true,
labelOrientation: "horizontal",
label: dynamicValue<string>("Test Label", false)
});

render(<Switch {...props} />);
const wrapper = screen.getByTestId(`${name}$wrapper`);
expect(wrapper.props.style).toEqual(expect.arrayContaining([{ flexDirection: "row", alignItems: "center" }]));

const horizontalContainer = screen.getByTestId(`${name}$horizontalContainer`);

expect(horizontalContainer.props.style).toEqual(
expect.objectContaining({ flexDirection: "row", alignItems: "center" })
);

expect(horizontalContainer).toContainElement(screen.getByTestId(`${name}$label`));
expect(horizontalContainer).toContainElement(screen.getByTestId(name));
});

it("with showLabel true and labelOrientation vertical, renders vertical", () => {
Expand All @@ -74,8 +84,9 @@
render(<Switch {...props} />);
const wrapper = screen.getByTestId(`${name}$wrapper`);
expect(wrapper.props.style).toEqual(
expect.not.arrayContaining([{ flexDirection: "row", alignItems: "center" }])
expect.arrayContaining([{ flexDirection: "column", alignItems: "flex-start" }])
);
expect(screen.queryByTestId(`${name}$horizontalContainer`)).toBeNull();
});

it("with error renders validation message", () => {
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion packages/pluggableWidgets/switch-native/src/package.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<package xmlns="http://www.mendix.com/package/1.0/">
<clientModule name="Switch" version="1.1.0" xmlns="http://www.mendix.com/clientModule/1.0/">
<clientModule name="Switch" version="1.1.1" xmlns="http://www.mendix.com/clientModule/1.0/">
<widgetFiles>
<widgetFile path="Switch.xml" />
</widgetFiles>
Expand Down
2 changes: 0 additions & 2 deletions packages/pluggableWidgets/switch-native/src/ui/Styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ export interface SwitchStyle extends Style {
export const defaultSwitchStyle: SwitchStyle = {
container: {
// All ViewStyle properties are allowed
paddingVertical: 4,
justifyContent: "center"
},
containerDisabled: {
Expand All @@ -50,6 +49,5 @@ export const defaultSwitchStyle: SwitchStyle = {
},
validationMessage: {
// All TextStyle properties are allowed
alignSelf: "stretch"
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import { ActionValue, DynamicValue, EditableValue } from "mendix";

export type LabelOrientationEnum = "horizontal" | "vertical";

export type LabelPositionEnum = "left" | "right";

export interface SwitchProps<Style> {
name: string;
style: Style[];
Expand All @@ -16,6 +18,7 @@ export interface SwitchProps<Style> {
showLabel: boolean;
label?: DynamicValue<string>;
labelOrientation: LabelOrientationEnum;
labelPosition: LabelPositionEnum;
}

export interface SwitchPreviewProps {
Expand All @@ -34,4 +37,5 @@ export interface SwitchPreviewProps {
showLabel: boolean;
label: string;
labelOrientation: LabelOrientationEnum;
labelPosition: LabelPositionEnum;
}
Loading