Casl: Breaking CSS Style of third party components

Created on 25 Jul 2019  路  7Comments  路  Source: stalniy/casl

Hi,

First of all, thanks for the awesome library.

I am unable to keep the style when of my sidebar when using <Can/>. I am using antd to create a sidebar in my app but when I try to conditionally show a menu item based on user's ability, the styles get broken.

How can I make use of <Can/>without breaking the css child combinator styles (.parent > .child > .child2)?

My Code:

<Sider>
    <div className="logo" />
    <Menu theme="dark" mode="inline" defaultSelectedKeys={['1']}>
        <Can I="view" a="applications"> 
            <Menu.Item key="1"> // broken styles as child combinator is not found
                <Icon type="app" />
                <span>Applications</span>
            </Menu.Item>
        </Can>
        <Menu.Item key="2">
            <Icon type="settings" />
            <span>Settings</span>
        </Menu.Item>
    </Menu>
</Sider>

The <li> element generated in case of <Can/> has class "undefined-item" instead of "ant-menu-item", as in case of normal <Menu.Item>.

Using the following versions:

"@casl/ability": "^3.1.2",
"@casl/react": "^1.0.3",
"react": "^16.8.6",
"antd": "^3.20.6",

Most helpful comment

Close this as there is nothing I can do more with this. You can check my example here: https://codesandbox.io/s/admiring-field-dxc5t

All 7 comments

Hi,

How did you create <Can> (createContextualCan or createBoundCan)?

export const Can = createCanBoundTo(ability);

where createCanBoundTo was imported from @casl/react.

I also tried the createContextualCan following the readme but met the same fate.

<Can> uses React Fragment if available. Probably what I can do is to check if there is exactly one child node then render it directly and if there are more then render in fragment

That would indeed solve my issue. Thanks!

After some inverstigation, there is nothing I can do with <Can> component to make this stuff work properly due to how rc-menu works. rc-menu is a lib which is used by antd for menus.

That <Menu> goes over all children, clones them and pass additional properties. When you add <Can> as a child of <Menu>, <Can> receive all props which were supposed to be passed into <Menu.Item>. That's why you can incorrect class and actually not only :) There are other state management callbacks which are passed to <Menu.Item> from <Menu>.

So, it's not a fault of <React.Fragment>. And that changes won't help. There is potential workaround which I don't like:

<Can ...>{(isAllowed, ability, ...restProps) => <Menu.Item {...restProps}>}</Can>

What can you do to make things work?

Instead of <Can> component you can get Ability instance directly from React's context API (but keep in mind, in this case you can't use ability.update() function. Instead you need to create a new Ability instance each time you want to change permissions). There are 2 options:

I updated @casl/react README with that suggestions.

Your code in this case will look like this:

<Sider>
    <div className="logo" />
    <Menu theme="dark" mode="inline" defaultSelectedKeys={['1']}>
        { ability.can('view', 'applications') &&
            <Menu.Item key="1">
                <Icon type="app" />
                <span>Applications</span>
            </Menu.Item>
        }
        <Menu.Item key="2">
            <Icon type="settings" />
            <span>Settings</span>
        </Menu.Item>
    </Menu>
</Sider>

Close this as there is nothing I can do more with this. You can check my example here: https://codesandbox.io/s/admiring-field-dxc5t

@stalniy Thanks for the help. I really appreciate you looking at it.

Was this page helpful?
0 / 5 - 0 ratings