Skip to content
Merged
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
58 changes: 13 additions & 45 deletions docs/knowledge-base/Styling.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -11,63 +11,31 @@

<TableOfContent />

## Styling UI5 Web Components for React components

You can change the appearance of the Web Components by using [CSS Variables](https://www.w3schools.com/Css/css3_variables.asp).
Per default, we are injecting the Horizon theme parameters as CSS Variables into the `<head>`.
For example, if you want to change the color of all texts that use the `--sapTextColor` variable, you can create an additional `style` tag with the following content:

```html
<style>
* {
--sapTextColor: limegreen;
}
</style>
```

<MessageStrip
hideCloseButton
design={MessageStripDesign.Critical}
children="Changing the value of a CSS Variable will affect theming capabilities, as it will be changed for all themes!"
/>

As a consequence, all HTML Elements in the subtree where this style was applied are now displaying their texts in `limegreen` instead of `#32363a` which would be the default value for Fiori 3.
You can change CSS Variables on any level - in the head, or on every single element by using either CSS classes or element style.
## Styling UI5 Web Components (for React) components

A full list of all supported CSS Variables can be found [here](https://github.com/UI5/webcomponents-react/blob/main/packages/base/src/styling/ThemingParameters.ts)
or in the [theming-base-content](https://github.com/SAP/theming-base-content/tree/master/content/Base/baseLib) repo.

## Scrollbars
UI5 Web Components for React follows the same styling approach as the core [UI5 Web Components](https://ui5.github.io/webcomponents/docs/advanced/styles/).
You can apply CSS variables, use the `::part` pseudo-element selectors, or apply styles directly on selected components (e.g. `Button`, `Title`, `Input`, etc. ) as described in the linked [documentation](https://ui5.github.io/webcomponents/docs/advanced/styles/).

Rendering our `ThemeProvider` will apply the Fiori styles to all scrollbars on the page.
If you want to opt-out of this behavior, you can add the `.ui5-content-native-scrollbars` class to the respective element to prevent the scrollbar styling from being applied.
Components currently only available in the `ui5/webcomponents-react` repo, are not necessarily web components. For these kind of components you can follow the standard styling approach of React.

<MessageStrip
design={MessageStripDesign.Information}
hideCloseButton
children="Due to the limited configuration options for scrollbars, you must apply this class to each scroll-container element individually."
design={MessageStripDesign.Critical}
hideCloseButton
children="While it's easily possible targeting DOM elements and CSS classes of components without a shadow root, modifying internal structures or styles is not officially supported and may break with future updates. Use such changes carefully."
/>

```tsx
<ObjectPage className="ui5-content-native-scrollbars">{children}</ObjectPage>
```
## Scrollbars

`@ui5/webcomponents` components come with globally applied scrollbar styles.
If you want to opt-out of this behavior, you can add the `.ui5-content-native-scrollbars` class to the `body` of the page. You can find out more about this in the [ui5/webcomponents documentation](https://ui5.github.io/webcomponents/docs/advanced/scrollbars-customization/).

## Style your own components

It's quite likely you have to create some custom components when you are building an app.
In order to reuse our central styling approach, you can import the `ThemingParameters` that contain the various CSS variables used in our theming, or use the CSS variables directly.

<MessageStrip
design={MessageStripDesign.Information}
hideCloseButton
children={
<>
You can find all <code>ThemingParameters</code>{' '}
<ui5-link href="./?path=/docs/knowledge-base-public-utils--docs#theming-parameters">here</ui5-link>. If you don't
want to use CSS-in-JS library, you can also just use the corresponding CSS variable.
</>
}
/>
A full list of all supported CSS Variables can be found [here](https://github.com/UI5/webcomponents-react/blob/main/packages/base/src/styling/ThemingParameters.ts)
or in the [theming-base-content](https://github.com/SAP/theming-base-content/tree/master/content/Base/baseLib) repo.

Check notice on line 38 in docs/knowledge-base/Styling.mdx

View check run for this annotation

In Solidarity / Inclusive Language

Match Found

Please consider an alternative to `master`. Possibilities include: `primary`, `main`, `leader`, `active`, `writer`
Raw output
/master/gi

You can then create a custom component by following this recipe:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -381,11 +381,22 @@ describe('AnalyticalTable', () => {
function doubleClickResizer(selector: string, columnName: string, outerWidth: number) {
cy.get(selector)
.realHover()
.should('have.css', 'background-color', cssVarToRgb('--sapContent_DragAndDropActiveColor'))
.should(($el) => {
// the CSS variable is applied too late in React 18.
if (!reactVersion.startsWith('18')) {
const color = getComputedStyle($el[0]).getPropertyValue('background-color');
expect(color).to.equal(cssVarToRgb('--sapContent_DragAndDropActiveColor'));
}
})
.dblclick()
// fallback
.realClick({ clickCount: 2 });
cy.get(`[data-column-id="${columnName}"]`).invoke('outerWidth').should('equal', outerWidth);

cy.get(`[data-column-id="${columnName}"]`)
.invoke('outerWidth')
.should(($width: number) => {
expect(Math.floor($width)).to.equal(outerWidth);
});
}

let resizeColumns = columns.map((el) => {
Expand Down Expand Up @@ -413,6 +424,8 @@ describe('AnalyticalTable', () => {
cy.get('[data-component-name="AnalyticalTableResizer"]').eq(0).as('resizer1');
cy.get('[data-component-name="AnalyticalTableResizer"]').eq(1).as('resizer2');

cy.document().its('fonts.status').should('equal', 'loaded');

doubleClickResizer('@resizer2', 'age', 476);
doubleClickResizer('@resizer1', 'name', 476);
// doubled call count because of fallback
Expand Down Expand Up @@ -444,6 +457,7 @@ describe('AnalyticalTable', () => {
);

cy.get('[data-component-name="AnalyticalTableBody"]').scrollTo('bottom');
cy.wait(50);
doubleClickResizer('@resizer1', 'name', 93);
cy.get('@resize').should('have.callCount', 10);

Expand All @@ -453,8 +467,8 @@ describe('AnalyticalTable', () => {

cy.mount(<AnalyticalTable data={dataFixed} columns={resizeColumns} />);
cy.wait(100);
doubleClickResizer('@resizer2', 'age', 472.75);
doubleClickResizer('@resizer1', 'name', 472.75);
doubleClickResizer('@resizer2', 'age', 472);
doubleClickResizer('@resizer1', 'name', 472);

cy.get('@resize').should('have.callCount', 10);

Expand Down Expand Up @@ -3371,7 +3385,7 @@ describe('AnalyticalTable', () => {
useEffect(() => {
tableRef.current.scrollTo(520);
}, []);
return <AnalyticalTable data={generateMoreData(500)} columns={columns} ref={tableRef} />;
return <AnalyticalTable data={generateMoreData(300)} columns={columns} ref={tableRef} />;
};
cy.mount(<ScrollTo />);
cy.findByText('Name-12').should('be.visible');
Expand All @@ -3382,7 +3396,7 @@ describe('AnalyticalTable', () => {
useEffect(() => {
tableRef.current.scrollToItem(12, { align: 'start' });
}, []);
return <AnalyticalTable data={generateMoreData(500)} columns={columns} ref={tableRef} />;
return <AnalyticalTable data={generateMoreData(300)} columns={columns} ref={tableRef} />;
};
cy.mount(<ScrollToItem />);
cy.findByText('Name-12').should('be.visible');
Expand All @@ -3395,7 +3409,7 @@ describe('AnalyticalTable', () => {
}, []);
return (
<AnalyticalTable
data={generateMoreData(500)}
data={generateMoreData(300)}
columns={[
...columns,
...new Array(100).fill('').map((_, index) => ({ id: `${index}`, Header: () => index })),
Expand All @@ -3414,7 +3428,7 @@ describe('AnalyticalTable', () => {
}, []);
return (
<AnalyticalTable
data={generateMoreData(500)}
data={generateMoreData(300)}
columns={new Array(100).fill('').map((_, index) => ({ id: `${index}`, Header: () => index }))}
ref={tableRef}
/>
Expand Down Expand Up @@ -3870,12 +3884,12 @@ describe('AnalyticalTable', () => {
cy.get('[data-component-name="AnalyticalTableContainer"]').should(
'have.css',
'width',
withVertScrollbar ? '1293px' : '1306px',
withVertScrollbar ? '1290px' : '1306px',
);
cy.get('[data-component-name="AnalyticalTableBody"]').should(
'have.css',
'width',
withVertScrollbar ? '1293px' : '1306px',
withVertScrollbar ? '1290px' : '1306px',
);

const _data2 = [
Expand Down Expand Up @@ -3942,32 +3956,31 @@ describe('AnalyticalTable', () => {
cy.get('[data-component-name="AnalyticalTableContainer"]').should(
'have.css',
'width',
withVertScrollbar ? '1293px' : '1306px',
);
cy.get('[data-component-name="AnalyticalTableBody"]').should(
'have.css',
'width',
withVertScrollbar ? '1293px' : '1306px',
withVertScrollbar ? '1290px' : '1306px',
);
cy.get('[data-component-name="AnalyticalTableBody"]').should(($el) => {
const width = Math.round(parseFloat($el.css('width')));
expect(width).to.equal(withVertScrollbar ? 1290 : 1306);
});

cy.mount(<AnalyticalTable data={data} columns={columns} visibleRows={visibleRows} />);
cy.get('[data-component-name="AnalyticalTableContainer"]').should(
'have.css',
'width',
withVertScrollbar ? '1293px' : '1306px',
withVertScrollbar ? '1290px' : '1306px',
);
cy.get('[data-component-name="AnalyticalTableBody"]').should(
'have.css',
'width',
withVertScrollbar ? '1293px' : '1306px',
withVertScrollbar ? '1290px' : '1306px',
);

const _columns3 = [...columns, { id: 'long', Header: 'Long', width: 2000, Cell: 'Long' }];
cy.mount(<AnalyticalTable data={data} columns={_columns3} visibleRows={visibleRows} />);
cy.get('[data-component-name="AnalyticalTableContainer"]').should(
'have.css',
'width',
withVertScrollbar ? '1293px' : '1306px',
withVertScrollbar ? '1290px' : '1306px',
);
cy.get('[data-component-name="AnalyticalTableBody"]').should('have.css', 'width', '2240px'); // 4 * 60 (minWidth) + 2000

Expand All @@ -3980,7 +3993,7 @@ describe('AnalyticalTable', () => {
cy.get('[data-component-name="AnalyticalTableContainer"]').should(
'have.css',
'width',
withVertScrollbar ? '1293px' : '1306px',
withVertScrollbar ? '1290px' : '1306px',
);
cy.get('[data-component-name="AnalyticalTableBody"]').should('have.css', 'width', '1440px'); // 4 * 60 (minWidth) + 1000 (maxWidth) + 200
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,13 +148,6 @@
overflow-y: auto;
scrollbar-width: none;
box-sizing: border-box;
-ms-overflow-style: none;

&::-webkit-scrollbar {
width: 0 !important;
height: 0 !important;
display: none !important;
}
}

.alternateRowColor {
Expand Down Expand Up @@ -600,56 +593,14 @@
background-color: var(--sapList_HeaderBackground);
border-inline-start: 1px solid var(--sapList_BorderColor);
margin-inline-start: -1px;
width: calc(var(--sapScrollBar_Dimension) + 2px);
}
.scrollbar {
box-sizing: border-box;
overflow-y: auto;
border-inline-end: var(--_ui5wcr-AnalyticalTable-OuterBorderInline);
border-block-end: 1px solid var(--sapList_TableFooterBorder);
border-inline-start: 1px solid var(--sapList_BorderColor);
margin-inline-start: -1px;
box-sizing: border-box;
}

.scrollbar:global(.ui5-content-native-scrollbars) {
background-color: var(--sapList_Background);
width: calc(var(--sapScrollBar_Dimension) + 2px);
}

.analyticalTableDelta:global(.ui5-content-native-scrollbars) {
&::-webkit-scrollbar {
background-color: var(--sapScrollBar_TrackColor);
-webkit-border-start: var(--_ui5wcr_Scrollbar_Border);
}

&::-webkit-scrollbar-thumb {
border-radius: var(--_ui5wcr_Scrollbar_BorderRadius);
background-color: var(--sapScrollBar_FaceColor);

&:hover {
background-color: var(--sapScrollBar_FaceColor);
}

&:horizontal {
height: var(--sapScrollBar_Dimension);
}

&:vertical {
width: var(--sapScrollBar_Dimension);
}
}

&::-webkit-scrollbar-corner {
background-color: var(--sapScrollBar_TrackColor);
}

&::-webkit-scrollbar:horizontal {
height: var(--sapScrollBar_Dimension);
}

&::-webkit-scrollbar:vertical {
width: var(--sapScrollBar_Dimension);
}
}

.verticalScroller {
Expand Down
5 changes: 0 additions & 5 deletions packages/main/src/components/AnalyticalTable/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,6 @@ const AnalyticalTable = forwardRef<AnalyticalTableDomRef, AnalyticalTablePropTyp
useStylesheet(styleData, AnalyticalTable.displayName);
const isInitialized = useRef(false);

const nativeScrollbar = className?.includes('ui5-content-native-scrollbars');
const alwaysShowSubComponent =
subComponentsBehavior === AnalyticalTableSubComponentsBehavior.Visible ||
subComponentsBehavior === AnalyticalTableSubComponentsBehavior.IncludeHeight;
Expand Down Expand Up @@ -676,9 +675,6 @@ const AnalyticalTable = forwardRef<AnalyticalTableDomRef, AnalyticalTablePropTyp
classNames.table,
withNavigationHighlight && classNames.hasNavigationIndicator,
showVerticalEndBorder && classNames.showVerticalEndBorder,
nativeScrollbar
? 'ui5-content-native-scrollbars'
: `ui5-content-native-scrollbars ${classNames.analyticalTableDelta}`,
);

const handleOnLoadMore = (e) => {
Expand Down Expand Up @@ -881,7 +877,6 @@ const AnalyticalTable = forwardRef<AnalyticalTableDomRef, AnalyticalTablePropTyp
tableRef={tableRef}
ref={verticalScrollBarRef}
scrollContainerRef={scrollContainerRef}
nativeScrollbar={nativeScrollbar}
classNames={classNames}
/>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,11 @@ interface VerticalScrollbarProps {
tableRef: MutableRefObject<HTMLDivElement>;
tableBodyHeight: number;
scrollContainerRef: MutableRefObject<HTMLDivElement>;
nativeScrollbar: boolean;
classNames: ClassNames;
}

export const VerticalScrollbar = forwardRef<HTMLDivElement, VerticalScrollbarProps>((props, ref) => {
const { internalRowHeight, tableRef, tableBodyHeight, scrollContainerRef, nativeScrollbar, classNames } = props;
const { internalRowHeight, tableRef, tableBodyHeight, scrollContainerRef, classNames } = props;
const hasHorizontalScrollbar = tableRef?.current?.offsetWidth !== tableRef?.current?.scrollWidth;
const horizontalScrollbarSectionStyles = clsx(hasHorizontalScrollbar && classNames.bottomSection);

Expand All @@ -36,12 +35,7 @@ export const VerticalScrollbar = forwardRef<HTMLDivElement, VerticalScrollbarPro
style={{
height: tableRef.current ? `${tableBodyHeight}px` : '0',
}}
className={clsx(
classNames.scrollbar,
nativeScrollbar
? 'ui5-content-native-scrollbars'
: `ui5-content-native-scrollbars ${classNames.analyticalTableDelta}`,
)}
className={clsx(classNames.scrollbar)}
data-component-name="AnalyticalTableVerticalScrollbar"
tabIndex={-1}
>
Expand Down
Loading