In my React Ag-grid, I have a column which contains text portion followed by an optional badge/image. Using tips from this post, we can add ellipsis to column and this works okay if the column only has text data.

For my scenario, the desired behavior is as follows and the ellipsis in textual part should move dynamically without hiding the badge, when user resizes column or window.

| small text (badge) |

| some lo... (badge) |

| long text here too |

When user expands column, it should look like

| small text here! (badge) |

| some longer t... (badge) |

| long text no badge here |

For this, I am thinking of adding custom cellRenderer for the column in columnDefs and then define it as a div of two spans (one for the ellipsis/nowrap text and the other for the optional badge/image).

However, to control the width of the textual span, I will likely have to have a UseEffect on column?.getActualWidth() and then maintain a state 'columnWidth' using which I can specify the text span's width.

const NoWrapText = styled('span', (props: { width: number }) => ({
    overflow: 'hidden',
    textOverflow: 'ellipsis',
    display: 'inline-block',
    whiteSpace: 'nowrap',
    maxWidth: props.width,
}));

export const CellRenderer = (props: ICellRendererParams) => {
    const [columnWidth, setColumnWidth] = useState(props.column?.getActualWidth());
    const isBadgeNeeded = checkIfBadgeIsNeededFunc(prop);
    const widthToSet = !isBadgeNeeded ? columnWidth - 40 : columnWidth - 100;
    
    useEffect(() => {
         const actualColumnWidth = props.column?.getActualWidth();
         if (actualColumnWidth) {
             setColumnWidth(actualColumnWidth);
         }
     }, [props.column?.getActualWidth()]);
    
    return (
     <div>
        <NoWrapText width={widthToSet}>props.value</NoWrapText>
        <span><Badge></Badge></span>
     </div>
     );
};


export const colDef = [
    {
        headerName: 'header name',
        enableRowGroup: true,
        field: 'name',
        rowGroupIndex: 2,
        hide: true,
        cellRenderer: 'cellRenderer',
    },
    {
        headerName: 'header id',
        enableRowGroup: true,
        field: 'id',
        rowGroupIndex: 1,
        hide: true,
        cellRenderer: 'cellRenderer',
    },
];

const onGridSizeChanged = (params: any): void => {
      if (gridParams.api) {
          params.api.redrawRows();
          params.api.sizeColumnsToFit();
      }
  };

I have to do this for all columns, so will have to store different state variables for each column.

Any better ways to achieve all of this?

1

There are 1 best solutions below

0
DustInComp On

Stretch the cell across the column's width using flexbox

.badge-icon {
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background-color: #ccc;
}
table {
  width: 50%;
  table-layout: fixed;
}

.cell {
  display: flex;
  align-items: center;
  justify-content: stretch;
}
.nowrap {
  flex: auto;
  width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
.badge {
  flex: none;
  margin-left: 4px;
}
<table>
  <tbody>
    <tr>
      <td>1</td>
      <td>
        <div class="cell">
          <span class="nowrap">Short text</span>
          <span class="badge"><div class="badge-icon"></div></span>
        </div>
      </td>
    </tr>
    <tr>
      <td>2</td>
      <td>
        <div class="cell">
          <span class="nowrap">Somewhat longer text without a badge</span>
        </div>
      </td>
    </tr>
    <tr>
      <td>3</td>
      <td>
        <div class="cell">
          <span class="nowrap">Longer text with a badge</span>
          <span class="badge"><div class="badge-icon"></div></span>
        </div>
      </td>
    </tr>
  </tbody>
</table>