Skip to content
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

feat(ObjectPage): refactor component to support ui5wc v2 #6089

Merged
merged 14 commits into from
Jul 19, 2024
77 changes: 66 additions & 11 deletions docs/MigrationGuide.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -627,33 +627,88 @@ The regular methods are now general purpose, so they can be used both inside the
### ObjectPage

The newly introduced `DynamicPage` web component comes with its own `DynamicPageHeader` and `DynamicPageTitle` components, which are unfortunately incompatible with our `ObjectPage` implementation.
Please use the following components instead:
Please use the `ObjectPageHeader` or `ObjectPageTitle` component instead.

- `headerContent` now only accepts the `ObjectPageHeader` component.
- `headerTitle` now only accepts the `ObjectPageTitle` component.
**Removed Props:**

- `showHideHeaderButton`: Hiding the expand/collapse button is not supported by design anymore.
- `showTitleInHeaderContent`: Showing the `headerTitle` as part of the `headerContent` is [not supported by design anymore](https://experience.sap.com/fiori-design-web/object-page/#dynamic-page-header-mandatory).

**Refactored Props:**

- `headerContent` has been renamed to `headerArea` and now only accepts the `ObjectPageHeader` component.
- `headerTitle` has been renamed to `titleArea` and now only accepts the `ObjectPageTitle` component.
- `headerContentPinnable` has been renamed to `hidePinButton` and the logic has been inverted. The pin button is now shown by default.

**Renamed Props:**

- `a11yConfig` has been renamed to `accessibilityAttributes`
- `a11yConfig.dynamicPageAnchorBar` has been renamed to `accessibilityAttributes.objectPageAnchorBar`
- `alwaysShowContentHeader` has been renamed to `headerPinned`
- `headerContentPinnable` has been renamed to `hidePinButton` and the logic has been inverted. The pin button is now shown by default.

**Removed Props:**

- `showHideHeaderButton`: Hiding the expand/collapse button is not supported by design anymore.
- `showTitleInHeaderContent`: Showing the `headerTitle` as part of the `headerContent` is [not supported by design anymore](https://experience.sap.com/fiori-design-web/object-page/#dynamic-page-header-mandatory).
- `footer` has been renamed to `footerArea`
- `onToggleHeaderContent` has been renamed to `onToggleHeaderArea`
- `onPinnedStateChange` has been renamed to `onPinButtonToggle`

Also, the namings of internal `data-component-name` attributes have been adjusted accordingly. E.g. `data-component-name="DynamicPageTitleSubHeader"` has been renamed to `data-component-name="ObjectPageTitleSubHeader"`

### ObjectPageTitle
### ObjectPageTitle (f.k.a. DynamicPageTitle)

_The `ObjectPageTitle` component is the renamed implementation of the old (React only) `DynamicPageTitle` component._
_The `ObjectPageTitle` component is the renamed implementation of the old (React only) `DynamicPageTitle` component. Now, it should only be used in the `ObjectPage`._

**Removed Props:**

- `actionsToolbarProps`: Since it's now recommended passing the `Toolbar` component directly, this prop is redundant.
- `navigationActionsToolbarProps`: Since it's now recommended passing the `Toolbar` component directly, this prop is redundant.
- `showSubHeaderRight`: Displaying the subheader in the same line as the header is not supported by design anymore.

**Refactored Props:**

- `actions` has been renamed to `actionsBar`. Instead of single actions, the `Toolbar` component should now be passed.
- `navigationActions` has been renamed to `navigationBar`. Instead of single actions, the `Toolbar` component should now be passed. The `ObjectPageTitle` still offers support for the legacy `Toolbar`.

_The `ObjectPageTitle` still offers support for the legacy `Toolbar`. You can find out more about this [here](?path=/docs/layouts-floorplans-objectpage--docs#legacy-toolbar-support)._

```jsx
// v1
<DynamicPageTitle
showSubHeaderRight
actionsToolbarProps={{ style: { background: 'red' } }}
navigationActionsToolbarProps={{ style: { background: 'red' } }}
actions={
<>
<Button>Action 1</Button>
<Button>Action 2</Button>
</>
}
navigationActions={
<>
<Button>Navigation-Action 1</Button>
<Button>Navigation-Action 2</Button>
</>
}
/>

// v2
<ObjectPageTitle
// `showSubHeaderRight` has been removed without replacement

// You can now pass all toolbar props directly to the toolbar,
// making `actionsToolbarProps` and `navigationActionsToolbarProps` redundant
actionsBar={
<Toolbar design="Transparent" style={{ background: 'red' }}>
<ToolbarButton text="Action 1" />
<ToolbarButton text="Action 2" />
</Toolbar>
}
navigationBar={
<Toolbar design="Transparent" style={{ background: 'red' }}>
<ToolbarButton text="Navigation-Action 1" />
<ToolbarButton text="Navigation-Action 2" />
</Toolbar>
}
/>
```

### ObjectPageSection

The prop `titleText` is now required and the default value `true` has been removed for the `titleTextUppercase` prop to comply with the updated Fiori design guidelines.
Expand Down
3 changes: 3 additions & 0 deletions packages/base/src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ export const enrichEventWithDetails = <
event: Event,
payload: Detail
): EnrichedEventType<Event, Detail> => {
if (!event) {
return event;
}
// todo: once we drop React 16 support, remove this
// the helper accepts both SyntheticEvents and browser events
const syntheticEventCast = event as unknown as SyntheticEvent;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -295,10 +295,16 @@
"changedProps": {
"a11yConfig": "accessibilityAttributes",
"alwaysShowContentHeader": "headerPinned",
"headerContentPinnable": "hidePinButton"
"headerContentPinnable": "hidePinButton",
"footer": "footerArea",
"onToggleHeaderContent": "onToggleHeaderArea",
"onPinnedStateChange": "onPinButtonToggle"
},
"removedProps": ["showHideHeaderButton", "showTitleInHeaderContent"]
},
"ObjectPageTitle": {
"removedProps": ["showSubHeaderRight"]
},
"ObjectStatus": {
"changedProps": {
"active": "interactive"
Expand Down
69 changes: 69 additions & 0 deletions packages/cli/src/scripts/codemod/transforms/v2/main.cts
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,75 @@ export default function transform(file: FileInfo, api: API, options?: Options):
headerContentPinnable.remove();
isDirty = true;
}

const headerContent = j(el).find(j.JSXAttribute, { name: { name: 'headerContent' } });
if (headerContent.size() > 0) {
const attr = headerContent.get();
if (attr.value.value && attr.value.value.type === 'JSXExpressionContainer') {
headerContent.forEach((el) => {
const dynamicPageHeader = j(el).find(j.JSXElement, {
openingElement: { name: { name: 'DynamicPageHeader' } }
});

// remove DynamicPageHeader import only if no DynamicPage is there
if (!componentIsImportedFromWebComponentsReact(j, root, 'DynamicPage')) {
const imports = root.find(j.ImportDeclaration);
const dptImport = imports.find(j.ImportSpecifier, { local: { name: 'DynamicPageHeader' } });
dptImport.remove();
}
if (!componentIsImportedFromWebComponentsReact(j, root, 'ObjectPageHeader')) {
Lukas742 marked this conversation as resolved.
Show resolved Hide resolved
addWebComponentsReactImport(j, root, 'ObjectPageHeader');
}

// replace JSX tag (component)
dynamicPageHeader.forEach((innerEl) => {
const updatedTagName = j.jsxIdentifier('ObjectPageHeader');
innerEl.node.openingElement.name = updatedTagName;
if (innerEl.node.closingElement) {
innerEl.node.closingElement.name = updatedTagName;
}
});

// replace prop name
headerContent.find(j.JSXIdentifier, { name: 'headerContent' }).replaceWith(j.jsxIdentifier('headerArea'));
});
isDirty = true;
}
}

const headerTitle = j(el).find(j.JSXAttribute, { name: { name: 'headerTitle' } });
if (headerTitle.size() > 0) {
const attr = headerTitle.get();
if (attr.value.value && attr.value.value.type === 'JSXExpressionContainer') {
headerTitle.forEach((el) => {
const dynamicPageTitle = j(el).find(j.JSXElement, {
openingElement: { name: { name: 'DynamicPageTitle' } }
});

// remove DynamicPageTitle import only if no DynamicPage is there
dynamicPageTitle.forEach((innerEl) => {
if (!componentIsImportedFromWebComponentsReact(j, root, 'DynamicPage')) {
const imports = root.find(j.ImportDeclaration);
const dptImport = imports.find(j.ImportSpecifier, { local: { name: 'DynamicPageTitle' } });
dptImport.remove();
}
if (!componentIsImportedFromWebComponentsReact(j, root, 'ObjectPageTitle')) {
addWebComponentsReactImport(j, root, 'ObjectPageTitle');
}
// replace JSX tag (component)
const updatedTagName = j.jsxIdentifier('ObjectPageTitle');
innerEl.node.openingElement.name = updatedTagName;
if (innerEl.node.closingElement) {
innerEl.node.closingElement.name = updatedTagName;
}
});

// replace prop name
headerTitle.find(j.JSXIdentifier, { name: 'headerTitle' }).replaceWith(j.jsxIdentifier('titleArea'));
});
isDirty = true;
}
}
});
}

Expand Down
8 changes: 8 additions & 0 deletions packages/compat/src/components/Toolbar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,8 @@ const Toolbar = forwardRef<HTMLDivElement, ToolbarPropTypes>((props, ref) => {
...rest
} = props;

const inObjectPage = props['data-in-object-page-title'];

useStylesheet(styleData, Toolbar.displayName);
const [componentRef, outerContainer] = useSyncRef<HTMLDivElement>(ref);
const controlMetaData = useRef([]);
Expand Down Expand Up @@ -317,6 +319,9 @@ const Toolbar = forwardRef<HTMLDivElement, ToolbarPropTypes>((props, ref) => {
}, [calculateVisibleItems]);

const handleToolbarClick = (e) => {
if (inObjectPage && typeof onClick === 'function') {
onClick(e);
}
if (active && typeof onClick === 'function') {
const isSpaceEnterDown = e.type === 'keydown' && (e.code === 'Enter' || e.code === 'Space');
if (isSpaceEnterDown && e.target !== e.currentTarget) {
Expand Down Expand Up @@ -379,6 +384,7 @@ const Toolbar = forwardRef<HTMLDivElement, ToolbarPropTypes>((props, ref) => {
tabIndex={active ? 0 : undefined}
role={active ? 'button' : undefined}
data-sap-ui-fastnavgroup="true"
data-component-name="Toolbar"
{...rest}
>
<div className={classNames.toolbar} data-component-name="ToolbarContent" ref={contentRef}>
Expand Down Expand Up @@ -420,4 +426,6 @@ const Toolbar = forwardRef<HTMLDivElement, ToolbarPropTypes>((props, ref) => {
});

Toolbar.displayName = 'Toolbar';
//@ts-expect-error: private identifier
Toolbar._displayName = 'UI5WCRToolbar';
export { Toolbar };
Loading
Loading