Hi <@U036Y1NKJMC>, I’ve already resolved a few of...
# extensions
b
Hi @busy-ability-54059, I’ve already resolved a few of the earlier issues—thanks for the help so far! I now need some guidance on the following: 1. I’m using
sortableTable
and would like to implement both
TableAction
and
RowActions
. Specifically, I’d like to add a delete button in the table header that deletes all selected rows, and also have a row-level delete action. Could you please share some guidance or examples on how to implement this effectively? 2. For the
<LabeledInput />
component, I want to pass an async validation function as a rule prop, similar to how we pass regular (sync) rules. What’s the proper way to do this? 3. I’m using
<YamlEditor />
inside
<Tab />
, and the tabs are rendered in a loop within the
<Tabbed />
component. Each tab has its own
YamlEditor
, but only the first editor displays its content by default. The other editors remain blank until I click inside them. Any ideas on how to fix this? (I’ve also attached an image for clarity.) Thanks in advance for your help—really appreciate the support!
b
Let dive into each of the topics: 1. you’ll need to “activate” the table actions and row actions in
SortableTable
. Use these props:
:table-actions="true"
and
:row-actions="true"
The rest is on the extension docs: https://extensions.rancher.io/extensions/next/api/actions#actionlocationtable-options 2. I don’t think it’s possible. I think the
rules
prop is for a very specific usage. You’ll probably need to do it on the page you are using it with the
@update:value
prop. You’ll need to build around it I think. If you want to use what we have for rules, you can check multiple examples like https://github.com/rancher/dashboard/blob/master/pkg/aks/components/Config.vue#L981 and dive into those functions 3. Needs investigation. Can you share the information contained in the array
previewFiles
so that I can try to replicate when I have some spare time? I don’t need a ton of entries. Just 3 or something (a simpler version of the array, but that still reproes the problem you’ve been having. You can DM me of you want
b
@busy-ability-54059 This is the definition of
preview
file,
Copy code
const previewFiles = ref<
  Array<{ key: string; filename: string; data: string }>
>([]);
where data is in yaml format like this,
Copy code
'version: "1.0"\napp:\n  name: demo-app\n  environment: production\n  features:\n    login: true\n    signup: true\n    analytics: false\ndatabase:\n  type: postgres\n  host: localhost\n  port: 5432\nusers:\n  - name: Alice\n    role: admin\n  - name: Bob\n    role: editor\n'
Regarding issue 1 — I’m working on a custom page (not a resource page), so I’m a bit unsure about where
plugin.addAction
should be used. Since I don’t have something like
{ resource: ['<http://catalog.cattle.io|catalog.cattle.io>.clusterrepo'] }
, I’m not sure if—or how—I can access the
plugin
instance directly from a Vue 3 component. Ideally, I’d like to keep everything self-contained within the Vue 3 component. What I’m aiming for is: • For bulk/table actions, I’d like to place some custom buttons in the top-left/top-right of the table header that perform actions based on the currently selected rows (with access to their context). • For inline/row actions, I’d like to use the three-dot menu to provide actions specific to each row, again with full row context. Is this kind of approach feasible in a custom page?
b
Replying to issue 1 - you need to add the custom table actions (via extension) from where you have the
plugin
object accessible which is the
index.ts/index.js
file of your extension, much like https://github.com/rancher/ui-plugin-examples/blob/main/pkg/extensions-api-demo/index.ts#L76-L104 As for the the second parameter called
when
in the docs, as per https://extensions.rancher.io/extensions/next/api/actions#addaction, it’s based on the LocationConfig object , which accepts many different params. One you could use for your custom page is the
path
. Check the others in the docs. The better you narrow it down, the least likely you’ll get unwanted side-effects
b
Regarding issues 1, This is my configuration and the output I'm getting. How to add a button on the header and inside three dots? Another issue is the row-wise checkbox is not working (cannot check/uncheck). Only the checkbox on the column header is working.
b
I think the targeting
LocationConfig
might be wrong on your code… I did:
Copy code
// TABLE ACTIONS - ROW ACTION
  plugin.addAction(
    ActionLocation.TABLE,
    { path: { urlPath: '/c/_/auth/management.cattle.io.user', exact: false } },
    {
      label: 'some-extension-action',
      invoke(opts: ActionOpts, values: any[]) {
        console.log('table action executed 1', this, opts, values); // eslint-disable-line no-console
      }
    }
  );

  // TABLE ACTIONS - ROW + BULKABLE
  plugin.addAction(
    ActionLocation.TABLE,
    { path: { urlPath: '/c/_/auth/management.cattle.io.user', exact: false } },
    {
      label:    'some-bulkable-action',
      multiple: true,
      invoke(opts: ActionOpts, values: any[]) {
        console.log('table action executed 2', this); // eslint-disable-line no-console
        console.log(opts); // eslint-disable-line no-console
        console.log(values); // eslint-disable-line no-console
      },
    }
  );
Targeting a table inside the user details -
Global Permissions
tab and it worked fine…. I did it in a way to NOT use
resource
or anything…. just path with
exact: false
(this works on my side because I hacked that sortableTable to have the
:table-actions="true"
and
:row-actions="true
) Try different variations of it
b
It's not working. Not sure how you are setting the path like that, as the path accepts an array of key-value pairs. And what would be the urlPath for this route?
Copy code
{
    name: `c-cluster-${YOUR_PRODUCT_NAME}-${CUSTOM_PAGE_NAME2}`,
    path: `/c/:cluster/${YOUR_PRODUCT_NAME}/${CUSTOM_PAGE_NAME2}`,
    component: Overview,
    meta: { product: YOUR_PRODUCT_NAME },
  },
b
`[{ urlPath:
/${YOUR_PRODUCT_NAME}/${CUSTOM_PAGE_NAME2}
exact: false }]`
b
I don't know why it's not working actually. Everything seems in place. I'm providing you my files. Can you please check and tell me what has gone wrong?
b
Okay, so I gave this a try and this really isn’t working because the items you are listing aren’t a resource and therefore doesn’t have a model like https://github.com/rancher/dashboard/tree/master/shell/models It needs to extend
Resource
so that it can work like https://github.com/rancher/dashboard/blob/master/shell/models/management.cattle.io.cluster.js#L32 (just an example, since
SteveModel
extends
Resource
from other classes, but the bare minimum is
Resource
class) this needs a bit of thought…. I don’t have an easy fix right now
b
Thanks for giving it a try and explaining the real issue. Let me know if you come up with something. Btw, I solved the 3rd issue with <YamlEditor/> using refresh and focus methods which exposed by the component.
👍 1
Should I create an issue/feature request on the dashboard repository? It's kind of a blocker situation from my side.
b
@breezy-airplane-74201 yes, please do, but I don’t think it’s likely that will be fixed anytime soon because of the whole complexity surrounding our tables and this mechanism. There would have to be a ton of refactoring to do…. As a workaround I was playing with a concept called
spoofedType
that we use in a couple of places in Rancher UI. It’s at the “same” level as configureType ( “pure” resource page ) or virtualType (custom page), but instead of creating a “pure” resource page it creates a fake resource. It’s useful in a couple of scenario, especially the ones where you need a list of some sort to be a resource and have an associated model, which is exactly what is needed here. We don’t advertise this in our docs as it’s a quite complex and advanced usecase, which also makes it difficult to explain. It’s nearly there, but I still can’t quite figure out what’s missing on the test that I did (i’ll include the extension code in a zip). It’s still not getting the
model
for this spoofed type and therefore not triggering the extension table actions. All of this is based on the work in a built-in extension for Harvester Manager , where we define a spoofed type for
HCI.CLUSTER
https://github.com/rancher/dashboard/blob/master/pkg/harvester-manager/config/harvester-manager.js#L130-L173, we have an associated model and then we render a custom list view (which can be anything, basically, as it’s a Vue page) which in theory should be targetable by the table actions extension point. Maybe you can continue the work by cloning https://github.com/rancher/dashboard, adding the extension inside the zip to the
pkg
folder https://github.com/rancher/dashboard/tree/master/pkg + running the Dashboard locally. FYI @stocky-account-63046
b
@busy-ability-54059 do you guys have anything like toaster? to show some temporary notifications/status
s
we have
growls
(kicked off by store action
growl/..
), however are moving away from them to use the new
Notifications
centre (currently kicked off by store action
notifications/..
) introduced in 2.12.0. i don't think we ever officially externalised growls and we haven't yet formalised the API for the notification centre, so both areas are liable to change